Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56dcda0d5e | ||
|
|
e44f0c403b | ||
|
|
fb1498bf7a | ||
|
|
fa69213d15 | ||
|
|
f92e50f35b | ||
|
|
a792b47b41 | ||
|
|
68ec7efde0 | ||
|
|
f314ffb036 | ||
|
|
b303a83a77 | ||
|
|
66d0b1e608 | ||
|
|
48a589eb79 | ||
|
|
fef300dd5b | ||
|
|
49a0b6f167 | ||
|
|
e5fdaadbd2 | ||
|
|
ab382dc256 | ||
|
|
2c66de4df2 | ||
|
|
9de33d4252 | ||
|
|
ff8375c6e1 | ||
| 3e22214bbd | |||
| b4e90db372 | |||
| c6b9e0c2d1 | |||
| aefab79307 | |||
| a6c08576a9 | |||
|
|
90d11b8692 | ||
|
|
c4b57fbcb2 | ||
|
|
e6dbe2a1ca | ||
|
|
774f316c8b | ||
|
|
823e9489d6 | ||
|
|
dc38bf1895 | ||
|
|
96b866a3a8 | ||
|
|
3b11bac2ad | ||
|
|
47caafd037 | ||
|
|
50e0509007 | ||
|
|
8920c4a170 | ||
|
|
bbf9d7e90f | ||
|
|
46f471a900 | ||
|
|
aa28f8d99c | ||
|
|
6a7e18b124 | ||
|
|
53329c46ff | ||
|
|
6b1aea9c04 | ||
|
|
edec9a8f91 | ||
|
|
6a9a447f86 | ||
|
|
5302c25feb | ||
|
|
a616ed1a10 | ||
|
|
f0b5aff3bb | ||
|
|
44b4736703 | ||
|
|
b1ae30dda8 | ||
|
|
0d687268c7 | ||
|
|
425a570261 | ||
|
|
4c8179ee12 | ||
|
|
5ae13f0bd7 | ||
|
|
3510152e36 | ||
|
|
8dfb805c62 | ||
|
|
a7080f5457 | ||
|
|
8b72d1c7ae | ||
|
|
8bc0275e74 | ||
|
|
0348aaac59 | ||
|
|
9712481bed | ||
|
|
b5f901b2d9 | ||
|
|
0e2a3e00f5 | ||
|
|
b282356e9e | ||
|
|
b075e3a1d5 | ||
|
|
e27189ea32 | ||
|
|
59e478464e | ||
|
|
e1c7b20898 | ||
|
|
2f78411c3d | ||
|
|
d1d3cad4b0 | ||
|
|
1735b26e66 | ||
|
|
65ed62d2f5 | ||
|
|
ec03f19650 | ||
|
|
8567324a19 | ||
|
|
be2df361ef | ||
|
|
a5085dde0c | ||
|
|
cef86d1140 | ||
|
|
94c45acf6b |
@@ -1,87 +0,0 @@
|
|||||||
name: release-nightly
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
goreleaser:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # all history for all branches and tags
|
|
||||||
- uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
- name: goreleaser
|
|
||||||
uses: goreleaser/goreleaser-action@v5
|
|
||||||
with:
|
|
||||||
distribution: goreleaser-pro
|
|
||||||
version: latest
|
|
||||||
args: release --nightly
|
|
||||||
env:
|
|
||||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
|
||||||
AWS_REGION: ${{ secrets.AWS_REGION }}
|
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
S3_REGION: ${{ secrets.AWS_REGION }}
|
|
||||||
S3_BUCKET: ${{ secrets.AWS_BUCKET }}
|
|
||||||
GORELEASER_FORCE_TOKEN: 'gitea'
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
release-image:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: catthehacker/ubuntu:act-latest
|
|
||||||
env:
|
|
||||||
DOCKER_ORG: gitea
|
|
||||||
DOCKER_LATEST: nightly
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # all history for all branches and tags
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker BuildX
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Get Meta
|
|
||||||
id: meta
|
|
||||||
run: |
|
|
||||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
|
||||||
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
platforms: |
|
|
||||||
linux/amd64
|
|
||||||
linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
|
|
||||||
|
|
||||||
- name: Build and push dind-rootless
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
env:
|
|
||||||
ACTIONS_RUNTIME_TOKEN: '' # See https://gitea.com/gitea/act_runner/issues/119
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile.rootless
|
|
||||||
platforms: |
|
|
||||||
linux/amd64
|
|
||||||
linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}-dind-rootless
|
|
||||||
@@ -1,98 +1,79 @@
|
|||||||
name: release-tag
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
goreleaser:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: linux-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
- goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
- goos: darwin
|
||||||
|
goarch: amd64
|
||||||
|
- goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
- goos: windows
|
||||||
|
goarch: amd64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # all history for all branches and tags
|
fetch-depth: 0
|
||||||
- uses: actions/setup-go@v5
|
|
||||||
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
- name: Import GPG key
|
cache: false
|
||||||
id: import_gpg
|
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
- name: Build
|
||||||
with:
|
|
||||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
||||||
passphrase: ${{ secrets.PASSPHRASE }}
|
|
||||||
fingerprint: CC64B1DB67ABBEECAB24B6455FC346329753F4B0
|
|
||||||
- name: goreleaser
|
|
||||||
uses: goreleaser/goreleaser-action@v5
|
|
||||||
with:
|
|
||||||
distribution: goreleaser-pro
|
|
||||||
version: latest
|
|
||||||
args: release
|
|
||||||
env:
|
env:
|
||||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
GOPRIVATE: git.marketally.com
|
||||||
AWS_REGION: ${{ secrets.AWS_REGION }}
|
VERSION: ${{ github.ref_name }}
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
S3_REGION: ${{ secrets.AWS_REGION }}
|
|
||||||
S3_BUCKET: ${{ secrets.AWS_BUCKET }}
|
|
||||||
GORELEASER_FORCE_TOKEN: 'gitea'
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
|
|
||||||
release-image:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: catthehacker/ubuntu:act-latest
|
|
||||||
env:
|
|
||||||
DOCKER_ORG: gitea
|
|
||||||
DOCKER_LATEST: latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # all history for all branches and tags
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker BuildX
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Get Meta
|
|
||||||
id: meta
|
|
||||||
run: |
|
run: |
|
||||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
# Strip the v prefix from tag
|
||||||
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
|
VERSION="${VERSION#v}"
|
||||||
|
EXT=""
|
||||||
|
if [ "${{ matrix.goos }}" = "windows" ]; then
|
||||||
|
EXT=".exe"
|
||||||
|
fi
|
||||||
|
echo "Building version: ${VERSION}"
|
||||||
|
CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} \
|
||||||
|
go build -a -ldflags "-X gitea.com/gitea/act_runner/internal/pkg/ver.version=${VERSION}" \
|
||||||
|
-o act_runner-${{ matrix.goos }}-${{ matrix.goarch }}${EXT}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Upload artifact
|
||||||
uses: docker/build-push-action@v5
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
name: act_runner-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||||
file: ./Dockerfile
|
path: act_runner-*
|
||||||
platforms: |
|
|
||||||
linux/amd64
|
|
||||||
linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
|
|
||||||
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
|
|
||||||
|
|
||||||
- name: Build and push dind-rootless
|
release:
|
||||||
uses: docker/build-push-action@v5
|
needs: build
|
||||||
|
runs-on: linux-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
|
||||||
|
- name: Prepare release files
|
||||||
|
run: |
|
||||||
|
mkdir -p release
|
||||||
|
find artifacts -type f -name 'act_runner-*' -exec mv {} release/ \;
|
||||||
|
cd release && sha256sum * > checksums.txt
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: release/*
|
||||||
|
generate_release_notes: true
|
||||||
env:
|
env:
|
||||||
ACTIONS_RUNTIME_TOKEN: '' # See https://gitea.com/gitea/act_runner/issues/119
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile.rootless
|
|
||||||
platforms: |
|
|
||||||
linux/amd64
|
|
||||||
linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}-dind-rootless
|
|
||||||
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}-dind-rootless
|
|
||||||
|
|||||||
@@ -1,20 +1,33 @@
|
|||||||
name: checks
|
name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
- push
|
push:
|
||||||
- pull_request
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
build-and-test:
|
||||||
name: check and test
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
- name: vet checks
|
cache: false
|
||||||
|
|
||||||
|
- name: Vet
|
||||||
run: make vet
|
run: make vet
|
||||||
- name: build
|
env:
|
||||||
|
GOPRIVATE: git.marketally.com
|
||||||
|
|
||||||
|
- name: Build
|
||||||
run: make build
|
run: make build
|
||||||
- name: test
|
env:
|
||||||
|
GOPRIVATE: git.marketally.com
|
||||||
|
|
||||||
|
- name: Test
|
||||||
run: make test
|
run: make test
|
||||||
|
env:
|
||||||
|
GOPRIVATE: git.marketally.com
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
act_runner
|
/act_runner
|
||||||
.env
|
.env
|
||||||
.runner
|
.runner
|
||||||
coverage.txt
|
coverage.txt
|
||||||
@@ -12,3 +12,4 @@ coverage.txt
|
|||||||
__debug_bin
|
__debug_bin
|
||||||
# gorelease binary folder
|
# gorelease binary folder
|
||||||
dist
|
dist
|
||||||
|
act_runner-*
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
@@ -14,6 +16,9 @@ builds:
|
|||||||
- amd64
|
- amd64
|
||||||
- arm
|
- arm
|
||||||
- arm64
|
- arm64
|
||||||
|
- loong64
|
||||||
|
- s390x
|
||||||
|
- riscv64
|
||||||
goarm:
|
goarm:
|
||||||
- "5"
|
- "5"
|
||||||
- "6"
|
- "6"
|
||||||
@@ -81,7 +86,7 @@ blobs:
|
|||||||
provider: s3
|
provider: s3
|
||||||
bucket: "{{ .Env.S3_BUCKET }}"
|
bucket: "{{ .Env.S3_BUCKET }}"
|
||||||
region: "{{ .Env.S3_REGION }}"
|
region: "{{ .Env.S3_REGION }}"
|
||||||
folder: "act_runner/{{.Version}}"
|
directory: "act_runner/{{.Version}}"
|
||||||
extra_files:
|
extra_files:
|
||||||
- glob: ./**.xz
|
- glob: ./**.xz
|
||||||
- glob: ./**.sha256
|
- glob: ./**.sha256
|
||||||
@@ -97,10 +102,10 @@ checksum:
|
|||||||
- glob: ./**.xz
|
- glob: ./**.xz
|
||||||
|
|
||||||
snapshot:
|
snapshot:
|
||||||
name_template: "{{ .Branch }}-devel"
|
version_template: "{{ .Branch }}-devel"
|
||||||
|
|
||||||
nightly:
|
nightly:
|
||||||
name_template: "nightly"
|
version_template: "nightly"
|
||||||
|
|
||||||
gitea_urls:
|
gitea_urls:
|
||||||
api: https://gitea.com/api/v1
|
api: https://gitea.com/api/v1
|
||||||
|
|||||||
58
Dockerfile
58
Dockerfile
@@ -1,16 +1,64 @@
|
|||||||
FROM golang:1.21-alpine3.18 as builder
|
### BUILDER STAGE
|
||||||
|
#
|
||||||
|
#
|
||||||
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
# Do not remove `git` here, it is required for getting runner version when executing `make build`
|
# Do not remove `git` here, it is required for getting runner version when executing `make build`
|
||||||
RUN apk add --no-cache make git
|
RUN apk add --no-cache make git
|
||||||
|
|
||||||
|
ARG GOPROXY
|
||||||
|
ENV GOPROXY=${GOPROXY:-}
|
||||||
|
|
||||||
COPY . /opt/src/act_runner
|
COPY . /opt/src/act_runner
|
||||||
WORKDIR /opt/src/act_runner
|
WORKDIR /opt/src/act_runner
|
||||||
|
|
||||||
RUN make clean && make build
|
RUN make clean && make build
|
||||||
|
|
||||||
FROM alpine:3.18
|
### DIND VARIANT
|
||||||
RUN apk add --no-cache git bash tini
|
#
|
||||||
|
#
|
||||||
|
FROM docker:28-dind AS dind
|
||||||
|
|
||||||
|
RUN apk add --no-cache s6 bash git tzdata
|
||||||
|
|
||||||
COPY --from=builder /opt/src/act_runner/act_runner /usr/local/bin/act_runner
|
COPY --from=builder /opt/src/act_runner/act_runner /usr/local/bin/act_runner
|
||||||
COPY scripts/run.sh /opt/act/run.sh
|
COPY scripts/run.sh /usr/local/bin/run.sh
|
||||||
|
COPY scripts/s6 /etc/s6
|
||||||
|
|
||||||
ENTRYPOINT ["/sbin/tini","--","/opt/act/run.sh"]
|
VOLUME /data
|
||||||
|
|
||||||
|
ENTRYPOINT ["s6-svscan","/etc/s6"]
|
||||||
|
|
||||||
|
### DIND-ROOTLESS VARIANT
|
||||||
|
#
|
||||||
|
#
|
||||||
|
FROM docker:28-dind-rootless AS dind-rootless
|
||||||
|
|
||||||
|
USER root
|
||||||
|
RUN apk add --no-cache s6 bash git tzdata
|
||||||
|
|
||||||
|
COPY --from=builder /opt/src/act_runner/act_runner /usr/local/bin/act_runner
|
||||||
|
COPY scripts/run.sh /usr/local/bin/run.sh
|
||||||
|
COPY scripts/s6 /etc/s6
|
||||||
|
|
||||||
|
VOLUME /data
|
||||||
|
|
||||||
|
RUN mkdir -p /data && chown -R rootless:rootless /etc/s6 /data
|
||||||
|
|
||||||
|
ENV DOCKER_HOST=unix:///run/user/1000/docker.sock
|
||||||
|
|
||||||
|
USER rootless
|
||||||
|
ENTRYPOINT ["s6-svscan","/etc/s6"]
|
||||||
|
|
||||||
|
### BASIC VARIANT
|
||||||
|
#
|
||||||
|
#
|
||||||
|
FROM alpine AS basic
|
||||||
|
RUN apk add --no-cache tini bash git tzdata
|
||||||
|
|
||||||
|
COPY --from=builder /opt/src/act_runner/act_runner /usr/local/bin/act_runner
|
||||||
|
COPY scripts/run.sh /usr/local/bin/run.sh
|
||||||
|
|
||||||
|
VOLUME /data
|
||||||
|
|
||||||
|
ENTRYPOINT ["/sbin/tini","--","run.sh"]
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
FROM golang:1.21-alpine3.18 as builder
|
|
||||||
# Do not remove `git` here, it is required for getting runner version when executing `make build`
|
|
||||||
RUN apk add --no-cache make git
|
|
||||||
|
|
||||||
COPY . /opt/src/act_runner
|
|
||||||
WORKDIR /opt/src/act_runner
|
|
||||||
|
|
||||||
RUN make clean && make build
|
|
||||||
|
|
||||||
FROM docker:dind-rootless
|
|
||||||
USER root
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
git bash supervisor
|
|
||||||
|
|
||||||
COPY --from=builder /opt/src/act_runner/act_runner /usr/local/bin/act_runner
|
|
||||||
COPY /scripts/supervisord.conf /etc/supervisord.conf
|
|
||||||
COPY /scripts/run.sh /opt/act/run.sh
|
|
||||||
COPY /scripts/rootless.sh /opt/act/rootless.sh
|
|
||||||
|
|
||||||
RUN mkdir /data \
|
|
||||||
&& chown rootless:rootless /data
|
|
||||||
|
|
||||||
USER rootless
|
|
||||||
ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
|
||||||
121
HOWTOSTART.md
Normal file
121
HOWTOSTART.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# How to Start a GitCaddy Runner
|
||||||
|
|
||||||
|
This guide explains how to set up and start a GitCaddy Actions runner (act_runner) to execute your CI/CD workflows.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- A Linux, macOS, or Windows machine
|
||||||
|
- Network access to your GitCaddy/Gitea instance
|
||||||
|
- (Optional) Docker installed for container-based workflows
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Download the Runner
|
||||||
|
|
||||||
|
Download the latest release from the [releases page](https://git.marketally.com/gitcaddy/act_runner/releases) or build from source:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.marketally.com/gitcaddy/act_runner.git
|
||||||
|
cd act_runner
|
||||||
|
make build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Register the Runner
|
||||||
|
|
||||||
|
Get a registration token from your GitCaddy instance:
|
||||||
|
- **Global runners**: Admin Area → Actions → Runners → Create Runner
|
||||||
|
- **Organization runners**: Organization Settings → Actions → Runners
|
||||||
|
- **Repository runners**: Repository Settings → Actions → Runners
|
||||||
|
|
||||||
|
Then register:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./act_runner register --no-interactive \
|
||||||
|
--instance https://your-gitea-instance.com \
|
||||||
|
--token YOUR_REGISTRATION_TOKEN \
|
||||||
|
--name my-runner \
|
||||||
|
--labels linux,ubuntu-latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Start the Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./act_runner daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
### Runner Labels
|
||||||
|
|
||||||
|
Labels determine which jobs the runner can execute. Configure labels during registration or edit them in the admin UI.
|
||||||
|
|
||||||
|
Common labels:
|
||||||
|
- `linux`, `linux-latest` - Linux runners
|
||||||
|
- `windows`, `windows-latest` - Windows runners
|
||||||
|
- `macos`, `macos-latest` - macOS runners
|
||||||
|
- `ubuntu`, `ubuntu-latest` - Ubuntu-specific
|
||||||
|
- `self-hosted` - Self-hosted runners
|
||||||
|
|
||||||
|
### Running as a Service
|
||||||
|
|
||||||
|
#### Linux (systemd)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cat > /etc/systemd/system/act_runner.service << 'SERVICE'
|
||||||
|
[Unit]
|
||||||
|
Description=GitCaddy Actions Runner
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=runner
|
||||||
|
WorkingDirectory=/opt/act_runner
|
||||||
|
ExecStart=/opt/act_runner/act_runner daemon
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SERVICE
|
||||||
|
|
||||||
|
sudo systemctl enable act_runner
|
||||||
|
sudo systemctl start act_runner
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Support
|
||||||
|
|
||||||
|
For workflows that use container actions, ensure Docker is installed and the runner user has access:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
```
|
||||||
|
|
||||||
|
## Capabilities Detection
|
||||||
|
|
||||||
|
The runner automatically detects and reports:
|
||||||
|
- Operating system and architecture
|
||||||
|
- Available shells (bash, sh, powershell)
|
||||||
|
- Installed tools (node, python, go, etc.)
|
||||||
|
- Docker availability
|
||||||
|
- Disk space and network bandwidth
|
||||||
|
|
||||||
|
These capabilities help admins understand what each runner can handle.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Runner not connecting
|
||||||
|
|
||||||
|
1. Check network connectivity to your GitCaddy instance
|
||||||
|
2. Verify the registration token is valid
|
||||||
|
3. Check firewall rules allow outbound HTTPS
|
||||||
|
|
||||||
|
### Jobs not running
|
||||||
|
|
||||||
|
1. Verify runner labels match the job's `runs-on` requirement
|
||||||
|
2. Check runner is online in the admin panel
|
||||||
|
3. Review runner logs: `journalctl -u act_runner -f`
|
||||||
|
|
||||||
|
## More Information
|
||||||
|
|
||||||
|
- [act_runner Repository](https://git.marketally.com/gitcaddy/act_runner)
|
||||||
|
- [GitCaddy Documentation](https://git.marketally.com/gitcaddy/gitea)
|
||||||
16
Makefile
16
Makefile
@@ -1,13 +1,12 @@
|
|||||||
DIST := dist
|
DIST := dist
|
||||||
EXECUTABLE := act_runner
|
EXECUTABLE := act_runner
|
||||||
GOFMT ?= gofumpt -l
|
GOFMT ?= gofumpt -l
|
||||||
DIST := dist
|
|
||||||
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
||||||
GO ?= go
|
GO ?= go
|
||||||
SHASUM ?= shasum -a 256
|
SHASUM ?= shasum -a 256
|
||||||
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||||
XGO_VERSION := go-1.18.x
|
XGO_VERSION := go-1.24.x
|
||||||
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
|
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
|
||||||
|
|
||||||
LINUX_ARCHS ?= linux/amd64,linux/arm64
|
LINUX_ARCHS ?= linux/amd64,linux/arm64
|
||||||
@@ -21,6 +20,8 @@ DOCKER_TAG ?= nightly
|
|||||||
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
|
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
|
||||||
DOCKER_ROOTLESS_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)-dind-rootless
|
DOCKER_ROOTLESS_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)-dind-rootless
|
||||||
|
|
||||||
|
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
|
||||||
|
|
||||||
ifneq ($(shell uname), Darwin)
|
ifneq ($(shell uname), Darwin)
|
||||||
EXTLDFLAGS = -extldflags "-static" $(null)
|
EXTLDFLAGS = -extldflags "-static" $(null)
|
||||||
else
|
else
|
||||||
@@ -102,7 +103,15 @@ fmt-check:
|
|||||||
exit 1; \
|
exit 1; \
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
test: fmt-check
|
.PHONY: deps-tools
|
||||||
|
deps-tools: ## install tool dependencies
|
||||||
|
$(GO) install $(GOVULNCHECK_PACKAGE)
|
||||||
|
|
||||||
|
.PHONY: security-check
|
||||||
|
security-check: deps-tools
|
||||||
|
GOEXPERIMENT= $(GO) run $(GOVULNCHECK_PACKAGE) -show color ./...
|
||||||
|
|
||||||
|
test: fmt-check security-check
|
||||||
@$(GO) test -v -cover -coverprofile coverage.txt ./... && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
@$(GO) test -v -cover -coverprofile coverage.txt ./... && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||||
|
|
||||||
.PHONY: vet
|
.PHONY: vet
|
||||||
@@ -170,7 +179,6 @@ docker:
|
|||||||
ARG_DISABLE_CONTENT_TRUST=--disable-content-trust=false; \
|
ARG_DISABLE_CONTENT_TRUST=--disable-content-trust=false; \
|
||||||
fi; \
|
fi; \
|
||||||
docker build $${ARG_DISABLE_CONTENT_TRUST} -t $(DOCKER_REF) .
|
docker build $${ARG_DISABLE_CONTENT_TRUST} -t $(DOCKER_REF) .
|
||||||
docker build $${ARG_DISABLE_CONTENT_TRUST} -t $(DOCKER_ROOTLESS_REF) -f Dockerfile.rootless .
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(GO) clean -x -i ./...
|
$(GO) clean -x -i ./...
|
||||||
|
|||||||
221
README.md
221
README.md
@@ -1,106 +1,195 @@
|
|||||||
# act runner
|
# GitCaddy Act Runner
|
||||||
|
|
||||||
Act runner is a runner for Gitea based on [Gitea fork](https://gitea.com/gitea/act) of [act](https://github.com/nektos/act).
|
A Gitea Actions runner with enhanced capability detection and reporting for AI-friendly workflow generation.
|
||||||
|
|
||||||
|
> **This is a GitCaddy fork** of [gitea.com/gitea/act_runner](https://gitea.com/gitea/act_runner) with runner capability discovery features.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Act Runner executes Gitea Actions workflows using [act](https://github.com/nektos/act). This fork adds automatic capability detection, enabling Gitea to expose runner capabilities via API for AI tools to query before generating workflows.
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- **Capability Detection**: Automatically detects OS, architecture, Docker support, available shells, and installed tools
|
||||||
|
- **Capability Reporting**: Reports capabilities to Gitea server during runner declaration
|
||||||
|
- **Full Compatibility**: Drop-in replacement for standard act_runner
|
||||||
|
- **Multi-Platform**: Supports Linux, macOS, and Windows
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Prerequisites
|
### Download Pre-built Binary
|
||||||
|
|
||||||
Docker Engine Community version is required for docker mode. To install Docker CE, follow the official [install instructions](https://docs.docker.com/engine/install/).
|
Download from [Releases](https://git.marketally.com/gitcaddy/act_runner/releases):
|
||||||
|
|
||||||
### Download pre-built binary
|
|
||||||
|
|
||||||
Visit [here](https://dl.gitea.com/act_runner/) and download the right version for your platform.
|
|
||||||
|
|
||||||
### Build from source
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Linux (amd64)
|
||||||
|
curl -L -o act_runner https://git.marketally.com/gitcaddy/act_runner/releases/download/v0.3.1-gitcaddy/act_runner-linux-amd64
|
||||||
|
chmod +x act_runner
|
||||||
|
|
||||||
|
# macOS (Apple Silicon)
|
||||||
|
curl -L -o act_runner https://git.marketally.com/gitcaddy/act_runner/releases/download/v0.3.1-gitcaddy/act_runner-darwin-arm64
|
||||||
|
chmod +x act_runner
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build from Source
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.marketally.com/gitcaddy/act_runner.git
|
||||||
|
cd act_runner
|
||||||
make build
|
make build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build a docker image
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
### 1. Enable Actions in Gitea
|
||||||
make docker
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quickstart
|
Add to your Gitea `app.ini`:
|
||||||
|
|
||||||
Actions are disabled by default, so you need to add the following to the configuration file of your Gitea instance to enable it:
|
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[actions]
|
[actions]
|
||||||
ENABLED=true
|
ENABLED = true
|
||||||
```
|
```
|
||||||
|
|
||||||
### Register
|
### 2. Register the Runner
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./act_runner register
|
./act_runner register \
|
||||||
|
--instance https://your-gitea-instance.com \
|
||||||
|
--token YOUR_RUNNER_TOKEN \
|
||||||
|
--name my-runner \
|
||||||
|
--labels ubuntu-latest,docker
|
||||||
```
|
```
|
||||||
|
|
||||||
And you will be asked to input:
|
### 3. Start the Runner
|
||||||
|
|
||||||
1. Gitea instance URL, like `http://192.168.8.8:3000/`. You should use your gitea instance ROOT_URL as the instance argument
|
|
||||||
and you should not use `localhost` or `127.0.0.1` as instance IP;
|
|
||||||
2. Runner token, you can get it from `http://192.168.8.8:3000/admin/actions/runners`;
|
|
||||||
3. Runner name, you can just leave it blank;
|
|
||||||
4. Runner labels, you can just leave it blank.
|
|
||||||
|
|
||||||
The process looks like:
|
|
||||||
|
|
||||||
```text
|
|
||||||
INFO Registering runner, arch=amd64, os=darwin, version=0.1.5.
|
|
||||||
WARN Runner in user-mode.
|
|
||||||
INFO Enter the Gitea instance URL (for example, https://gitea.com/):
|
|
||||||
http://192.168.8.8:3000/
|
|
||||||
INFO Enter the runner token:
|
|
||||||
fe884e8027dc292970d4e0303fe82b14xxxxxxxx
|
|
||||||
INFO Enter the runner name (if set empty, use hostname: Test.local):
|
|
||||||
|
|
||||||
INFO Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-20.04:docker://node:16-bullseye,ubuntu-18.04:docker://node:16-buster,linux_arm:host):
|
|
||||||
|
|
||||||
INFO Registering runner, name=Test.local, instance=http://192.168.8.8:3000/, labels=[ubuntu-latest:docker://node:16-bullseye ubuntu-22.04:docker://node:16-bullseye ubuntu-20.04:docker://node:16-bullseye ubuntu-18.04:docker://node:16-buster].
|
|
||||||
DEBU Successfully pinged the Gitea instance server
|
|
||||||
INFO Runner registered successfully.
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also register with command line arguments.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./act_runner register --instance http://192.168.8.8:3000 --token <my_runner_token> --no-interactive
|
|
||||||
```
|
|
||||||
|
|
||||||
If the registry succeed, it will run immediately. Next time, you could run the runner directly.
|
|
||||||
|
|
||||||
### Run
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./act_runner daemon
|
./act_runner daemon
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run with docker
|
On startup, the runner will:
|
||||||
|
1. Detect system capabilities (OS, arch, Docker, shells, tools)
|
||||||
|
2. Report capabilities to Gitea via the Declare API
|
||||||
|
3. Begin polling for jobs
|
||||||
|
|
||||||
```bash
|
## Capability Detection
|
||||||
docker run -e GITEA_INSTANCE_URL=https://your_gitea.com -e GITEA_RUNNER_REGISTRATION_TOKEN=<your_token> -v /var/run/docker.sock:/var/run/docker.sock --name my_runner gitea/act_runner:nightly
|
|
||||||
|
The runner automatically detects:
|
||||||
|
|
||||||
|
| Category | Examples |
|
||||||
|
|----------|----------|
|
||||||
|
| **OS/Arch** | linux/amd64, darwin/arm64, windows/amd64 |
|
||||||
|
| **Container Runtime** | Docker, Podman |
|
||||||
|
| **Shells** | bash, sh, zsh, powershell, cmd |
|
||||||
|
| **Tools** | Node.js, Go, Python, Java, .NET, Rust |
|
||||||
|
| **Features** | Cache support, Docker Compose |
|
||||||
|
|
||||||
|
### Example Capabilities JSON
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"os": "linux",
|
||||||
|
"arch": "amd64",
|
||||||
|
"docker": true,
|
||||||
|
"docker_compose": true,
|
||||||
|
"container_runtime": "docker",
|
||||||
|
"shell": ["bash", "sh"],
|
||||||
|
"tools": {
|
||||||
|
"node": ["18.19.0"],
|
||||||
|
"go": ["1.21.5"],
|
||||||
|
"python": ["3.11.6"]
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"cache": true,
|
||||||
|
"docker_services": true
|
||||||
|
},
|
||||||
|
"limitations": []
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configuration
|
## Configuration
|
||||||
|
|
||||||
You can also configure the runner with a configuration file.
|
Create a config file or use command-line flags:
|
||||||
The configuration file is a YAML file, you can generate a sample configuration file with `./act_runner generate-config`.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./act_runner generate-config > config.yaml
|
./act_runner generate-config > config.yaml
|
||||||
|
./act_runner -c config.yaml daemon
|
||||||
```
|
```
|
||||||
|
|
||||||
You can specify the configuration file path with `-c`/`--config` argument.
|
Example configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
log:
|
||||||
|
level: info
|
||||||
|
|
||||||
|
runner:
|
||||||
|
file: .runner
|
||||||
|
capacity: 1
|
||||||
|
timeout: 3h
|
||||||
|
labels:
|
||||||
|
- ubuntu-latest:docker://node:18-bullseye
|
||||||
|
- ubuntu-22.04:docker://ubuntu:22.04
|
||||||
|
|
||||||
|
container:
|
||||||
|
docker_host: ""
|
||||||
|
force_pull: false
|
||||||
|
privileged: false
|
||||||
|
|
||||||
|
cache:
|
||||||
|
enabled: true
|
||||||
|
dir: ~/.cache/actcache
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Deployment
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./act_runner -c config.yaml register # register with config file
|
docker run -d \
|
||||||
./act_runner -c config.yaml daemon # run with config file
|
--name act_runner \
|
||||||
|
-e GITEA_INSTANCE_URL=https://your-gitea.com \
|
||||||
|
-e GITEA_RUNNER_REGISTRATION_TOKEN=<token> \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v ./data:/data \
|
||||||
|
gitcaddy/act_runner:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example Deployments
|
## GitCaddy Integration
|
||||||
|
|
||||||
Check out the [examples](examples) directory for sample deployment types.
|
This runner is designed to work with the [GitCaddy Gitea fork](https://git.marketally.com/gitcaddy/gitea), which provides:
|
||||||
|
|
||||||
|
- **Runner Capabilities API** (`/api/v2/repos/{owner}/{repo}/actions/runners/capabilities`)
|
||||||
|
- **Workflow Validation API** for pre-flight checks
|
||||||
|
- **Action Compatibility Database** for GitHub Actions mapping
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
act_runner Gitea AI Tool
|
||||||
|
| | |
|
||||||
|
| Declare + Capabilities | |
|
||||||
|
|---------------------------->| |
|
||||||
|
| | |
|
||||||
|
| | GET /api/v2/.../caps |
|
||||||
|
| |<------------------------|
|
||||||
|
| | |
|
||||||
|
| | Runner capabilities |
|
||||||
|
| |------------------------>|
|
||||||
|
| | |
|
||||||
|
| | Generates workflow |
|
||||||
|
| | with correct config |
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Projects
|
||||||
|
|
||||||
|
| Project | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| [gitcaddy/gitea](https://git.marketally.com/gitcaddy/gitea) | Gitea with AI-friendly enhancements |
|
||||||
|
| [gitcaddy/actions-proto-go](https://git.marketally.com/gitcaddy/actions-proto-go) | Protocol definitions with capability support |
|
||||||
|
|
||||||
|
## Upstream
|
||||||
|
|
||||||
|
This project is a fork of [gitea.com/gitea/act_runner](https://gitea.com/gitea/act_runner). We contribute enhancements back to upstream where appropriate.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License - see [LICENSE](LICENSE) for details.
|
||||||
|
|||||||
BIN
act_runner_test
Executable file
BIN
act_runner_test
Executable file
Binary file not shown.
@@ -5,12 +5,27 @@
|
|||||||
gitea:
|
gitea:
|
||||||
image: gitea/gitea
|
image: gitea/gitea
|
||||||
...
|
...
|
||||||
|
healthcheck:
|
||||||
|
# checks availability of Gitea's front-end with curl
|
||||||
|
test: ["CMD", "curl", "-f", "<instance_url>"]
|
||||||
|
interval: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
timeout: 10s
|
||||||
|
environment:
|
||||||
|
# GITEA_RUNNER_REGISTRATION_TOKEN can be used to set a global runner registration token.
|
||||||
|
# The Gitea version must be v1.23 or higher.
|
||||||
|
# It's also possible to use GITEA_RUNNER_REGISTRATION_TOKEN_FILE to pass the location.
|
||||||
|
# - GITEA_RUNNER_REGISTRATION_TOKEN=<user-defined registration token>
|
||||||
|
|
||||||
runner:
|
runner:
|
||||||
image: gitea/act_runner
|
image: gitea/act_runner
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- gitea
|
gitea:
|
||||||
|
# required so runner can attach to gitea, see "healthcheck"
|
||||||
|
condition: service_healthy
|
||||||
|
restart: true
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/act_runner:/data
|
- ./data/act_runner:/data
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ spec:
|
|||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
data:
|
data:
|
||||||
|
# The registration token can be obtained from the web UI, API or command-line.
|
||||||
|
# You can also set a pre-defined global runner registration token for the Gitea instance via
|
||||||
|
# `GITEA_RUNNER_REGISTRATION_TOKEN`/`GITEA_RUNNER_REGISTRATION_TOKEN_FILE` environment variable.
|
||||||
token: << base64 encoded registration token >>
|
token: << base64 encoded registration token >>
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
@@ -46,7 +49,7 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- name: runner
|
- name: runner
|
||||||
image: gitea/act_runner:nightly
|
image: gitea/act_runner:nightly
|
||||||
command: ["sh", "-c", "while ! nc -z localhost 2376 </dev/null; do echo 'waiting for docker daemon...'; sleep 5; done; /sbin/tini -- /opt/act/run.sh"]
|
command: ["sh", "-c", "while ! nc -z localhost 2376 </dev/null; do echo 'waiting for docker daemon...'; sleep 5; done; /sbin/tini -- run.sh"]
|
||||||
env:
|
env:
|
||||||
- name: DOCKER_HOST
|
- name: DOCKER_HOST
|
||||||
value: tcp://localhost:2376
|
value: tcp://localhost:2376
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ spec:
|
|||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
data:
|
data:
|
||||||
token: << runner registration token goes here >>
|
# The registration token can be obtained from the web UI, API or command-line.
|
||||||
|
# You can also set a pre-defined global runner registration token for the Gitea instance via
|
||||||
|
# `GITEA_RUNNER_REGISTRATION_TOKEN`/`GITEA_RUNNER_REGISTRATION_TOKEN_FILE` environment variable.
|
||||||
|
token: << base64 encoded registration token >>
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: runner-secret
|
name: runner-secret
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ As `root`:
|
|||||||
```bash
|
```bash
|
||||||
useradd -m rootless
|
useradd -m rootless
|
||||||
passwd rootless
|
passwd rootless
|
||||||
```
|
apt-get install -y uidmap # Not mentioned but needed for docker rootless.
|
||||||
|
```
|
||||||
|
|
||||||
- Install [`docker-ce`](https://docs.docker.com/engine/install/)
|
- Install [`docker-ce`](https://docs.docker.com/engine/install/)
|
||||||
- (Recommended) Disable the system-wide Docker daemon
|
- (Recommended) Disable the system-wide Docker daemon
|
||||||
@@ -21,12 +22,19 @@ As `root`:
|
|||||||
As the `rootless` user:
|
As the `rootless` user:
|
||||||
|
|
||||||
- Follow the instructions for [enabling rootless mode](https://docs.docker.com/engine/security/rootless/)
|
- Follow the instructions for [enabling rootless mode](https://docs.docker.com/engine/security/rootless/)
|
||||||
- Add the following lines to the `/home/rootless/.bashrc`:
|
- Add the following line to the `/home/rootless/.bashrc`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export XDG_RUNTIME_DIR=/home/rootless/.docker/run
|
for f in ./.bashrc.d/*.bash; do echo "Processing $f file..."; . "$f"; done
|
||||||
export PATH=/home/rootless/bin:$PATH
|
```
|
||||||
export DOCKER_HOST=unix:///run/user/1001/docker.sock
|
|
||||||
|
- Create the .bashrc.d directory `mkdir ~/.bashrc.d`
|
||||||
|
- Add the following lines to the `/home/rootless/.bashrc.d/rootless-docker.bash`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export XDG_RUNTIME_DIR=/home/rootless/.docker/run
|
||||||
|
export PATH=/home/rootless/bin:$PATH
|
||||||
|
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
- Reboot. Ensure that the Docker process is working.
|
- Reboot. Ensure that the Docker process is working.
|
||||||
|
|||||||
100
go.mod
100
go.mod
@@ -1,56 +1,61 @@
|
|||||||
module gitea.com/gitea/act_runner
|
module gitea.com/gitea/act_runner
|
||||||
|
|
||||||
go 1.21
|
go 1.24.0
|
||||||
|
|
||||||
|
toolchain go1.24.11
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.gitea.io/actions-proto-go v0.4.0
|
code.gitea.io/actions-proto-go v0.5.2
|
||||||
code.gitea.io/gitea-vet v0.2.3
|
code.gitea.io/gitea-vet v0.2.3
|
||||||
connectrpc.com/connect v1.15.0
|
connectrpc.com/connect v1.16.2
|
||||||
github.com/avast/retry-go/v4 v4.5.1
|
github.com/avast/retry-go/v4 v4.6.0
|
||||||
github.com/docker/docker v25.0.3+incompatible
|
github.com/docker/docker v25.0.13+incompatible
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/nektos/act v0.0.0 // will be replaced
|
github.com/nektos/act v0.0.0 // will be replaced
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.11.1
|
||||||
golang.org/x/term v0.18.0
|
golang.org/x/term v0.36.0
|
||||||
golang.org/x/time v0.5.0
|
golang.org/x/time v0.12.0
|
||||||
google.golang.org/protobuf v1.33.0
|
google.golang.org/protobuf v1.35.2
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gotest.tools/v3 v3.5.1
|
gotest.tools/v3 v3.5.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.37.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cyphar.com/go-pathrs v0.2.1 // indirect
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||||
github.com/Masterminds/semver v1.5.0 // indirect
|
github.com/Masterminds/semver v1.5.0 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||||
github.com/cloudflare/circl v1.3.7 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
github.com/containerd/containerd v1.7.13 // indirect
|
github.com/containerd/containerd v1.7.29 // indirect
|
||||||
github.com/containerd/log v0.1.0 // indirect
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
github.com/creack/pty v1.1.21 // indirect
|
github.com/creack/pty v1.1.21 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/distribution/reference v0.5.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/docker/cli v25.0.3+incompatible // indirect
|
github.com/docker/cli v25.0.3+incompatible // indirect
|
||||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.8.1 // indirect
|
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||||
github.com/docker/go-connections v0.5.0 // indirect
|
github.com/docker/go-connections v0.5.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/fatih/color v1.16.0 // indirect
|
github.com/fatih/color v1.17.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||||
github.com/go-git/go-git/v5 v5.11.0 // indirect
|
github.com/go-git/go-git/v5 v5.12.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/imdario/mergo v0.3.16 // indirect
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
@@ -58,45 +63,52 @@ require (
|
|||||||
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.7 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/moby/buildkit v0.12.5 // indirect
|
github.com/moby/buildkit v0.12.5 // indirect
|
||||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
github.com/moby/sys/user v0.1.0 // indirect
|
github.com/moby/sys/user v0.3.0 // indirect
|
||||||
|
github.com/moby/sys/userns v0.1.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
github.com/opencontainers/selinux v1.13.0 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rhysd/actionlint v1.6.27 // indirect
|
github.com/rhysd/actionlint v1.7.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||||
github.com/sergi/go-diff v1.3.1 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/timshannon/bolthold v0.0.0-20231129192944-dca5178aa629 // indirect
|
github.com/timshannon/bolthold v0.0.0-20240314194003-30aac6950928 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
go.etcd.io/bbolt v1.3.9 // indirect
|
go.etcd.io/bbolt v1.3.10 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.23.1 // indirect
|
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.23.1 // indirect
|
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.23.1 // indirect
|
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||||
golang.org/x/crypto v0.19.0 // indirect
|
golang.org/x/crypto v0.43.0 // indirect
|
||||||
golang.org/x/mod v0.15.0 // indirect
|
golang.org/x/net v0.45.0 // indirect
|
||||||
golang.org/x/net v0.21.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/sync v0.6.0 // indirect
|
golang.org/x/tools v0.23.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
|
||||||
golang.org/x/tools v0.18.0 // indirect
|
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/nektos/act => gitea.com/gitea/act v0.260.2
|
replace github.com/nektos/act => gitea.com/gitea/act v0.261.7-0.20251202193638-5417d3ac6742
|
||||||
|
|
||||||
|
replace github.com/go-git/go-git/v5 => github.com/go-git/go-git/v5 v5.16.2
|
||||||
|
|
||||||
|
// Remove after
|
||||||
|
replace github.com/distribution/reference v0.6.0 => github.com/distribution/reference v0.5.0
|
||||||
|
|
||||||
|
// Use GitCaddy fork with capability support
|
||||||
|
replace code.gitea.io/actions-proto-go => git.marketally.com/gitcaddy/actions-proto-go v0.5.7
|
||||||
|
|||||||
252
go.sum
252
go.sum
@@ -1,13 +1,15 @@
|
|||||||
code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU=
|
|
||||||
code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
|
|
||||||
code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI=
|
code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI=
|
||||||
code.gitea.io/gitea-vet v0.2.3/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
code.gitea.io/gitea-vet v0.2.3/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
||||||
connectrpc.com/connect v1.15.0 h1:lFdeCbZrVVDydAqwr4xGV2y+ULn+0Z73s5JBj2LikWo=
|
connectrpc.com/connect v1.16.2 h1:ybd6y+ls7GOlb7Bh5C8+ghA6SvCBajHwxssO2CGFjqE=
|
||||||
connectrpc.com/connect v1.15.0/go.mod h1:bQmjpDY8xItMnttnurVgOkHUBMRT9cpsNi2O4AjKhmA=
|
connectrpc.com/connect v1.16.2/go.mod h1:n2kgwskMHXC+lVqb18wngEpF95ldBHXjZYJussz5FRc=
|
||||||
|
cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
|
||||||
|
cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
|
||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
gitea.com/gitea/act v0.260.2 h1:2lDa5YD3ZaObLj1y5P5Hhb04enoQWRO6SR3reN7Acoo=
|
git.marketally.com/gitcaddy/actions-proto-go v0.5.7 h1:RUbafr3Vkw2l4WfSwa+oF+Ihakbm05W0FlAmXuQrDJc=
|
||||||
gitea.com/gitea/act v0.260.2/go.mod h1:Y2YN/C069qVJrGnmvN3WEfnI3OvBm4DdXPnWBXs+rHI=
|
git.marketally.com/gitcaddy/actions-proto-go v0.5.7/go.mod h1:RPu21UoRD3zSAujoZR6LJwuVNa2uFRBveadslczCRfQ=
|
||||||
|
gitea.com/gitea/act v0.261.7-0.20251202193638-5417d3ac6742 h1:ulcquQluJbmNASkh6ina70LvcHEa9eWYfQ+DeAZ0VEE=
|
||||||
|
gitea.com/gitea/act v0.261.7-0.20251202193638-5417d3ac6742/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||||
@@ -15,33 +17,31 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
|
|||||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
|
github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ=
|
||||||
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
|
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
|
||||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o=
|
github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA=
|
||||||
github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc=
|
github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
|
||||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
github.com/containerd/containerd v1.7.29 h1:90fWABQsaN9mJhGkoVnuzEY+o1XDPbg9BTC9QTAHnuE=
|
||||||
github.com/containerd/containerd v1.7.13 h1:wPYKIeGMN8vaggSKuV1X0wZulpMz4CrgEsZdaCyB6Is=
|
github.com/containerd/containerd v1.7.29/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs=
|
||||||
github.com/containerd/containerd v1.7.13/go.mod h1:zT3up6yTRfEUa6+GsITYIJNgSVL9NQ4x4h1RPzk0Wu4=
|
|
||||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -51,47 +51,47 @@ github.com/docker/cli v25.0.3+incompatible h1:KLeNs7zws74oFuVhgZQ5ONGZiXUUdgsdy6
|
|||||||
github.com/docker/cli v25.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v25.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
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/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
|
github.com/docker/docker v25.0.13+incompatible h1:YeBrkUd3q0ZoRDNoEzuopwCLU+uD8GZahDHwBdsTnkU=
|
||||||
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v25.0.13+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
|
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||||
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||||
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
|
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||||
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
|
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
|
||||||
@@ -112,8 +112,8 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
|
|||||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
@@ -136,45 +136,47 @@ github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkV
|
|||||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||||
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||||
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||||
|
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||||
|
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||||
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
|
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
|
||||||
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
|
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||||
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
github.com/opencontainers/selinux v1.13.0 h1:Zza88GWezyT7RLql12URvoxsbLfjFx988+LGaWfbL84=
|
||||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
github.com/opencontainers/selinux v1.13.0/go.mod h1:XxWTed+A/s5NNq4GmYScVy+9jzXhGBVEOAyucdRUY8s=
|
||||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rhysd/actionlint v1.6.27 h1:xxwe8YmveBcC8lydW6GoHMGmB6H/MTqUU60F2p10wjw=
|
github.com/rhysd/actionlint v1.7.1 h1:WJaDzyT1StBWVKGSsZPYnbV0HF9Y9/vD6KFdZQL42qE=
|
||||||
github.com/rhysd/actionlint v1.6.27/go.mod h1:m2nFUjAnOrxCMXuOMz9evYBRCLUsMnKY2IJl/N5umbk=
|
github.com/rhysd/actionlint v1.7.1/go.mod h1:lNjNNlZY0BdBl8l837Z9ZiBpu8v+5lzfoJQFdSk4xss=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
|
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||||
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -189,10 +191,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/timshannon/bolthold v0.0.0-20231129192944-dca5178aa629 h1:6JyscwjLxdI0S7GTDtcQXpxjsldBYwNXi4jfSpZEMzE=
|
github.com/timshannon/bolthold v0.0.0-20240314194003-30aac6950928 h1:zjNCuOOhh1TKRU0Ru3PPPJt80z7eReswCao91gBLk00=
|
||||||
github.com/timshannon/bolthold v0.0.0-20231129192944-dca5178aa629/go.mod h1:PCFYfAEfKT+Nd6zWvUpsXduMR1bXFLf0uGSlEF05MCI=
|
github.com/timshannon/bolthold v0.0.0-20240314194003-30aac6950928/go.mod h1:PCFYfAEfKT+Nd6zWvUpsXduMR1bXFLf0uGSlEF05MCI=
|
||||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
@@ -205,61 +207,50 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ
|
|||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||||
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
|
||||||
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
|
||||||
go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M=
|
go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||||
go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY=
|
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||||
go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA=
|
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||||
go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo=
|
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||||
go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI=
|
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||||
go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8=
|
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||||
go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI=
|
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
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=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||||
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
|
|
||||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -269,58 +260,43 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
|
||||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
|
||||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||||
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
|
|
||||||
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
|
google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
|
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
|
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||||
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
|
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||||
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
|||||||
@@ -39,15 +39,18 @@ func Execute(ctx context.Context) {
|
|||||||
registerCmd.Flags().StringVar(®Args.Token, "token", "", "Runner token")
|
registerCmd.Flags().StringVar(®Args.Token, "token", "", "Runner token")
|
||||||
registerCmd.Flags().StringVar(®Args.RunnerName, "name", "", "Runner name")
|
registerCmd.Flags().StringVar(®Args.RunnerName, "name", "", "Runner name")
|
||||||
registerCmd.Flags().StringVar(®Args.Labels, "labels", "", "Runner tags, comma separated")
|
registerCmd.Flags().StringVar(®Args.Labels, "labels", "", "Runner tags, comma separated")
|
||||||
|
registerCmd.Flags().BoolVar(®Args.Ephemeral, "ephemeral", false, "Configure the runner to be ephemeral and only ever be able to pick a single job (stricter than --once)")
|
||||||
rootCmd.AddCommand(registerCmd)
|
rootCmd.AddCommand(registerCmd)
|
||||||
|
|
||||||
// ./act_runner daemon
|
// ./act_runner daemon
|
||||||
|
var daemArgs daemonArgs
|
||||||
daemonCmd := &cobra.Command{
|
daemonCmd := &cobra.Command{
|
||||||
Use: "daemon",
|
Use: "daemon",
|
||||||
Short: "Run as a runner daemon",
|
Short: "Run as a runner daemon",
|
||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(0),
|
||||||
RunE: runDaemon(ctx, &configFile),
|
RunE: runDaemon(ctx, &daemArgs, &configFile),
|
||||||
}
|
}
|
||||||
|
daemonCmd.Flags().BoolVar(&daemArgs.Once, "once", false, "Run one job then exit")
|
||||||
rootCmd.AddCommand(daemonCmd)
|
rootCmd.AddCommand(daemonCmd)
|
||||||
|
|
||||||
// ./act_runner exec
|
// ./act_runner exec
|
||||||
|
|||||||
@@ -5,13 +5,16 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"connectrpc.com/connect"
|
"connectrpc.com/connect"
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
@@ -27,7 +30,21 @@ import (
|
|||||||
"gitea.com/gitea/act_runner/internal/pkg/ver"
|
"gitea.com/gitea/act_runner/internal/pkg/ver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runDaemon(ctx context.Context, configFile *string) func(cmd *cobra.Command, args []string) error {
|
const (
|
||||||
|
// DiskSpaceWarningThreshold is the percentage at which to warn about low disk space
|
||||||
|
DiskSpaceWarningThreshold = 85.0
|
||||||
|
// DiskSpaceCriticalThreshold is the percentage at which to log critical warnings
|
||||||
|
DiskSpaceCriticalThreshold = 95.0
|
||||||
|
// CapabilitiesUpdateInterval is how often to update capabilities (including disk space)
|
||||||
|
CapabilitiesUpdateInterval = 5 * time.Minute
|
||||||
|
// BandwidthTestInterval is how often to run bandwidth tests (hourly)
|
||||||
|
BandwidthTestInterval = 1 * time.Hour
|
||||||
|
)
|
||||||
|
|
||||||
|
// Global bandwidth manager - accessible for triggering manual tests
|
||||||
|
var bandwidthManager *envcheck.BandwidthManager
|
||||||
|
|
||||||
|
func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) func(cmd *cobra.Command, args []string) error {
|
||||||
return func(cmd *cobra.Command, args []string) error {
|
return func(cmd *cobra.Command, args []string) error {
|
||||||
cfg, err := config.LoadDefault(*configFile)
|
cfg, err := config.LoadDefault(*configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -63,7 +80,34 @@ func runDaemon(ctx context.Context, configFile *string) func(cmd *cobra.Command,
|
|||||||
log.Warn("no labels configured, runner may not be able to pick up jobs")
|
log.Warn("no labels configured, runner may not be able to pick up jobs")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ls.RequireDocker() {
|
if ls.RequireDocker() || cfg.Container.RequireDocker {
|
||||||
|
// Wait for dockerd be ready
|
||||||
|
if timeout := cfg.Container.DockerTimeout; timeout > 0 {
|
||||||
|
tctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
|
defer cancel()
|
||||||
|
keepRunning := true
|
||||||
|
for keepRunning {
|
||||||
|
dockerSocketPath, err := getDockerSocketPath(cfg.Container.DockerHost)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to get socket path: %s", err.Error())
|
||||||
|
} else if err = envcheck.CheckIfDockerRunning(tctx, dockerSocketPath); errors.Is(err, context.Canceled) {
|
||||||
|
log.Infof("Docker wait timeout of %s expired", timeout.String())
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
log.Errorf("Docker connection failed: %s", err.Error())
|
||||||
|
} else {
|
||||||
|
log.Infof("Docker is ready")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
case <-tctx.Done():
|
||||||
|
log.Infof("Docker wait timeout of %s expired", timeout.String())
|
||||||
|
keepRunning = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Require dockerd be ready
|
||||||
dockerSocketPath, err := getDockerSocketPath(cfg.Container.DockerHost)
|
dockerSocketPath, err := getDockerSocketPath(cfg.Container.DockerHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -88,6 +132,14 @@ func runDaemon(ctx context.Context, configFile *string) func(cmd *cobra.Command,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !slices.Equal(reg.Labels, ls.ToStrings()) {
|
||||||
|
reg.Labels = ls.ToStrings()
|
||||||
|
if err := config.SaveRegistration(cfg.Runner.File, reg); err != nil {
|
||||||
|
return fmt.Errorf("failed to save runner config: %w", err)
|
||||||
|
}
|
||||||
|
log.Infof("labels updated to: %v", reg.Labels)
|
||||||
|
}
|
||||||
|
|
||||||
cli := client.New(
|
cli := client.New(
|
||||||
reg.Address,
|
reg.Address,
|
||||||
cfg.Runner.Insecure,
|
cfg.Runner.Insecure,
|
||||||
@@ -97,67 +149,200 @@ func runDaemon(ctx context.Context, configFile *string) func(cmd *cobra.Command,
|
|||||||
)
|
)
|
||||||
|
|
||||||
runner := run.NewRunner(cfg, reg, cli)
|
runner := run.NewRunner(cfg, reg, cli)
|
||||||
|
|
||||||
|
// Detect runner capabilities for AI-friendly workflow generation
|
||||||
|
dockerHost := cfg.Container.DockerHost
|
||||||
|
if dockerHost == "" {
|
||||||
|
if dh, err := getDockerSocketPath(""); err == nil {
|
||||||
|
dockerHost = dh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize bandwidth manager with the Gitea server address
|
||||||
|
bandwidthManager = envcheck.NewBandwidthManager(reg.Address, BandwidthTestInterval)
|
||||||
|
bandwidthManager.Start(ctx)
|
||||||
|
log.Infof("bandwidth manager started, testing against: %s", reg.Address)
|
||||||
|
|
||||||
|
capabilities := envcheck.DetectCapabilities(ctx, dockerHost, cfg.Container.WorkdirParent)
|
||||||
|
// Include initial bandwidth result if available
|
||||||
|
capabilities.Bandwidth = bandwidthManager.GetLastResult()
|
||||||
|
capabilitiesJson := capabilities.ToJSON()
|
||||||
|
log.Infof("detected capabilities: %s", capabilitiesJson)
|
||||||
|
|
||||||
|
// Check disk space and warn if low
|
||||||
|
checkDiskSpaceWarnings(capabilities)
|
||||||
|
|
||||||
// declare the labels of the runner before fetching tasks
|
// declare the labels of the runner before fetching tasks
|
||||||
resp, err := runner.Declare(ctx, ls.Names())
|
resp, err := runner.Declare(ctx, ls.Names(), capabilitiesJson)
|
||||||
if err != nil && connect.CodeOf(err) == connect.CodeUnimplemented {
|
if err != nil && connect.CodeOf(err) == connect.CodeUnimplemented {
|
||||||
// Gitea instance is older version. skip declare step.
|
log.Errorf("Your Gitea version is too old to support runner declare, please upgrade to v1.21 or later")
|
||||||
log.Warn("Because the Gitea instance is an old version, skip declare labels and version.")
|
return err
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.WithError(err).Error("fail to invoke Declare")
|
log.WithError(err).Error("fail to invoke Declare")
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
log.Infof("runner: %s, with version: %s, with labels: %v, declare successfully",
|
log.Infof("runner: %s, with version: %s, with labels: %v, declare successfully",
|
||||||
resp.Msg.Runner.Name, resp.Msg.Runner.Version, resp.Msg.Runner.Labels)
|
resp.Msg.Runner.Name, resp.Msg.Runner.Version, resp.Msg.Runner.Labels)
|
||||||
// if declare successfully, override the labels in the.runner file with valid labels in the config file (if specified)
|
|
||||||
reg.Labels = ls.ToStrings()
|
|
||||||
if err := config.SaveRegistration(cfg.Runner.File, reg); err != nil {
|
|
||||||
return fmt.Errorf("failed to save runner config: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
poller := poll.New(cfg, cli, runner)
|
// Start periodic capabilities update goroutine
|
||||||
|
go periodicCapabilitiesUpdate(ctx, runner, ls.Names(), dockerHost, cfg.Container.WorkdirParent)
|
||||||
|
// Start periodic stale job cache cleanup (every hour, remove caches older than 2 hours)
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(1 * time.Hour)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
runner.CleanStaleJobCaches(2 * time.Hour)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
poller.Poll(ctx)
|
poller := poll.New(cfg, cli, runner)
|
||||||
|
poller.SetBandwidthManager(bandwidthManager)
|
||||||
|
|
||||||
|
if daemArgs.Once || reg.Ephemeral {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
poller.PollOnce()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// shutdown when we complete a job or cancel is requested
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
go poller.Poll()
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("runner: %s shutdown initiated, waiting %s for running jobs to complete before shutting down", resp.Msg.Runner.Name, cfg.Runner.ShutdownTimeout)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), cfg.Runner.ShutdownTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err = poller.Shutdown(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("runner: %s cancelled in progress jobs during shutdown", resp.Msg.Runner.Name)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkDiskSpaceWarnings logs warnings if disk space is low
|
||||||
|
func checkDiskSpaceWarnings(capabilities *envcheck.RunnerCapabilities) {
|
||||||
|
if capabilities.Disk == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
usedPercent := capabilities.Disk.UsedPercent
|
||||||
|
freeGB := float64(capabilities.Disk.Free) / (1024 * 1024 * 1024)
|
||||||
|
|
||||||
|
if usedPercent >= DiskSpaceCriticalThreshold {
|
||||||
|
log.Errorf("CRITICAL: Disk space critically low! %.1f%% used, only %.2f GB free. Runner may fail to execute jobs!", usedPercent, freeGB)
|
||||||
|
} else if usedPercent >= DiskSpaceWarningThreshold {
|
||||||
|
log.Warnf("WARNING: Disk space running low. %.1f%% used, %.2f GB free. Consider cleaning up disk space.", usedPercent, freeGB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// periodicCapabilitiesUpdate periodically updates capabilities including disk space and bandwidth
|
||||||
|
func periodicCapabilitiesUpdate(ctx context.Context, runner *run.Runner, labelNames []string, dockerHost string, workingDir string) {
|
||||||
|
ticker := time.NewTicker(CapabilitiesUpdateInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
log.Debug("stopping periodic capabilities update")
|
||||||
|
if bandwidthManager != nil {
|
||||||
|
bandwidthManager.Stop()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
// Detect updated capabilities (disk space changes over time)
|
||||||
|
capabilities := envcheck.DetectCapabilities(ctx, dockerHost, workingDir)
|
||||||
|
|
||||||
|
// Include latest bandwidth result
|
||||||
|
if bandwidthManager != nil {
|
||||||
|
capabilities.Bandwidth = bandwidthManager.GetLastResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
capabilitiesJson := capabilities.ToJSON()
|
||||||
|
|
||||||
|
// Check for disk space warnings
|
||||||
|
checkDiskSpaceWarnings(capabilities)
|
||||||
|
|
||||||
|
// Send updated capabilities to server
|
||||||
|
_, err := runner.Declare(ctx, labelNames, capabilitiesJson)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Debug("failed to update capabilities")
|
||||||
|
} else {
|
||||||
|
bandwidthInfo := ""
|
||||||
|
if capabilities.Bandwidth != nil {
|
||||||
|
bandwidthInfo = fmt.Sprintf(", bandwidth: %.1f Mbps", capabilities.Bandwidth.DownloadMbps)
|
||||||
|
}
|
||||||
|
log.Debugf("capabilities updated: disk %.1f%% used, %.2f GB free%s",
|
||||||
|
capabilities.Disk.UsedPercent,
|
||||||
|
float64(capabilities.Disk.Free)/(1024*1024*1024),
|
||||||
|
bandwidthInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type daemonArgs struct {
|
||||||
|
Once bool
|
||||||
|
}
|
||||||
|
|
||||||
// initLogging setup the global logrus logger.
|
// initLogging setup the global logrus logger.
|
||||||
func initLogging(cfg *config.Config) {
|
func initLogging(cfg *config.Config) {
|
||||||
|
callPrettyfier := func(f *runtime.Frame) (string, string) {
|
||||||
|
// get function name
|
||||||
|
s := strings.Split(f.Function, ".")
|
||||||
|
funcname := "[" + s[len(s)-1] + "]"
|
||||||
|
// get file name and line number
|
||||||
|
_, filename := path.Split(f.File)
|
||||||
|
filename = "[" + filename + ":" + strconv.Itoa(f.Line) + "]"
|
||||||
|
return funcname, filename
|
||||||
|
}
|
||||||
|
|
||||||
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
||||||
format := &log.TextFormatter{
|
format := &log.TextFormatter{
|
||||||
DisableColors: !isTerm,
|
DisableColors: !isTerm,
|
||||||
FullTimestamp: true,
|
FullTimestamp: true,
|
||||||
|
CallerPrettyfier: callPrettyfier,
|
||||||
}
|
}
|
||||||
log.SetFormatter(format)
|
log.SetFormatter(format)
|
||||||
|
|
||||||
if l := cfg.Log.Level; l != "" {
|
l := cfg.Log.Level
|
||||||
level, err := log.ParseLevel(l)
|
if l == "" {
|
||||||
if err != nil {
|
log.Infof("Log level not set, sticking to info")
|
||||||
log.WithError(err).
|
return
|
||||||
Errorf("invalid log level: %q", l)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// debug level
|
level, err := log.ParseLevel(l)
|
||||||
if level == log.DebugLevel {
|
if err != nil {
|
||||||
log.SetReportCaller(true)
|
log.WithError(err).
|
||||||
format.CallerPrettyfier = func(f *runtime.Frame) (string, string) {
|
Errorf("invalid log level: %q", l)
|
||||||
// get function name
|
}
|
||||||
s := strings.Split(f.Function, ".")
|
|
||||||
funcname := "[" + s[len(s)-1] + "]"
|
|
||||||
// get file name and line number
|
|
||||||
_, filename := path.Split(f.File)
|
|
||||||
filename = "[" + filename + ":" + strconv.Itoa(f.Line) + "]"
|
|
||||||
return funcname, filename
|
|
||||||
}
|
|
||||||
log.SetFormatter(format)
|
|
||||||
}
|
|
||||||
|
|
||||||
if log.GetLevel() != level {
|
// debug level
|
||||||
log.Infof("log level changed to %v", level)
|
switch level {
|
||||||
log.SetLevel(level)
|
case log.DebugLevel, log.TraceLevel:
|
||||||
}
|
log.SetReportCaller(true) // Only in debug or trace because it takes a performance toll
|
||||||
|
log.Infof("Log level %s requested, setting up report caller for further debugging", level)
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.GetLevel() != level {
|
||||||
|
log.Infof("log level set to %v", level)
|
||||||
|
log.SetLevel(level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ type executeArgs struct {
|
|||||||
envs []string
|
envs []string
|
||||||
envfile string
|
envfile string
|
||||||
secrets []string
|
secrets []string
|
||||||
|
vars []string
|
||||||
defaultActionsURL string
|
defaultActionsURL string
|
||||||
insecureSecrets bool
|
insecureSecrets bool
|
||||||
privileged bool
|
privileged bool
|
||||||
@@ -130,6 +131,22 @@ func (i *executeArgs) LoadEnvs() map[string]string {
|
|||||||
return envs
|
return envs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *executeArgs) LoadVars() map[string]string {
|
||||||
|
vars := make(map[string]string)
|
||||||
|
if i.vars != nil {
|
||||||
|
for _, runVar := range i.vars {
|
||||||
|
e := strings.SplitN(runVar, `=`, 2)
|
||||||
|
if len(e) == 2 {
|
||||||
|
vars[e[0]] = e[1]
|
||||||
|
} else {
|
||||||
|
vars[e[0]] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vars
|
||||||
|
}
|
||||||
|
|
||||||
// Workdir returns path to workdir
|
// Workdir returns path to workdir
|
||||||
func (i *executeArgs) Workdir() string {
|
func (i *executeArgs) Workdir() string {
|
||||||
return i.resolve(".")
|
return i.resolve(".")
|
||||||
@@ -386,6 +403,7 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command
|
|||||||
LogOutput: true,
|
LogOutput: true,
|
||||||
JSONLogger: execArgs.jsonLogger,
|
JSONLogger: execArgs.jsonLogger,
|
||||||
Env: execArgs.LoadEnvs(),
|
Env: execArgs.LoadEnvs(),
|
||||||
|
Vars: execArgs.LoadVars(),
|
||||||
Secrets: execArgs.LoadSecrets(),
|
Secrets: execArgs.LoadSecrets(),
|
||||||
InsecureSecrets: execArgs.insecureSecrets,
|
InsecureSecrets: execArgs.insecureSecrets,
|
||||||
Privileged: execArgs.privileged,
|
Privileged: execArgs.privileged,
|
||||||
@@ -468,6 +486,7 @@ func loadExecCmd(ctx context.Context) *cobra.Command {
|
|||||||
execCmd.Flags().StringArrayVarP(&execArg.envs, "env", "", []string{}, "env to make available to actions with optional value (e.g. --env myenv=foo or --env myenv)")
|
execCmd.Flags().StringArrayVarP(&execArg.envs, "env", "", []string{}, "env to make available to actions with optional value (e.g. --env myenv=foo or --env myenv)")
|
||||||
execCmd.PersistentFlags().StringVarP(&execArg.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers")
|
execCmd.PersistentFlags().StringVarP(&execArg.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers")
|
||||||
execCmd.Flags().StringArrayVarP(&execArg.secrets, "secret", "s", []string{}, "secret to make available to actions with optional value (e.g. -s mysecret=foo or -s mysecret)")
|
execCmd.Flags().StringArrayVarP(&execArg.secrets, "secret", "s", []string{}, "secret to make available to actions with optional value (e.g. -s mysecret=foo or -s mysecret)")
|
||||||
|
execCmd.Flags().StringArrayVarP(&execArg.vars, "var", "", []string{}, "variable to make available to actions with optional value (e.g. --var myvar=foo or --var myvar)")
|
||||||
execCmd.PersistentFlags().BoolVarP(&execArg.insecureSecrets, "insecure-secrets", "", false, "NOT RECOMMENDED! Doesn't hide secrets while printing logs.")
|
execCmd.PersistentFlags().BoolVarP(&execArg.insecureSecrets, "insecure-secrets", "", false, "NOT RECOMMENDED! Doesn't hide secrets while printing logs.")
|
||||||
execCmd.Flags().BoolVar(&execArg.privileged, "privileged", false, "use privileged mode")
|
execCmd.Flags().BoolVar(&execArg.privileged, "privileged", false, "use privileged mode")
|
||||||
execCmd.Flags().StringVar(&execArg.usernsMode, "userns", "", "user namespace to use")
|
execCmd.Flags().StringVar(&execArg.usernsMode, "userns", "", "user namespace to use")
|
||||||
@@ -484,7 +503,7 @@ func loadExecCmd(ctx context.Context) *cobra.Command {
|
|||||||
execCmd.PersistentFlags().BoolVarP(&execArg.noSkipCheckout, "no-skip-checkout", "", false, "Do not skip actions/checkout")
|
execCmd.PersistentFlags().BoolVarP(&execArg.noSkipCheckout, "no-skip-checkout", "", false, "Do not skip actions/checkout")
|
||||||
execCmd.PersistentFlags().BoolVarP(&execArg.debug, "debug", "d", false, "enable debug log")
|
execCmd.PersistentFlags().BoolVarP(&execArg.debug, "debug", "d", false, "enable debug log")
|
||||||
execCmd.PersistentFlags().BoolVarP(&execArg.dryrun, "dryrun", "n", false, "dryrun mode")
|
execCmd.PersistentFlags().BoolVarP(&execArg.dryrun, "dryrun", "n", false, "dryrun mode")
|
||||||
execCmd.PersistentFlags().StringVarP(&execArg.image, "image", "i", "node:16-bullseye", "Docker image to use. Use \"-self-hosted\" to run directly on the host.")
|
execCmd.PersistentFlags().StringVarP(&execArg.image, "image", "i", "docker.gitea.com/runner-images:ubuntu-latest", "Docker image to use. Use \"-self-hosted\" to run directly on the host.")
|
||||||
execCmd.PersistentFlags().StringVarP(&execArg.network, "network", "", "", "Specify the network to which the container will connect")
|
execCmd.PersistentFlags().StringVarP(&execArg.network, "network", "", "", "Specify the network to which the container will connect")
|
||||||
execCmd.PersistentFlags().StringVarP(&execArg.githubInstance, "gitea-instance", "", "", "Gitea instance to use.")
|
execCmd.PersistentFlags().StringVarP(&execArg.githubInstance, "gitea-instance", "", "", "Gitea instance to use.")
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func runRegister(ctx context.Context, regArgs *registerArgs, configFile *string)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
go func() {
|
go func() {
|
||||||
if err := registerInteractive(ctx, *configFile); err != nil {
|
if err := registerInteractive(ctx, *configFile, regArgs); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -75,6 +75,7 @@ type registerArgs struct {
|
|||||||
Token string
|
Token string
|
||||||
RunnerName string
|
RunnerName string
|
||||||
Labels string
|
Labels string
|
||||||
|
Ephemeral bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type registerStage int8
|
type registerStage int8
|
||||||
@@ -91,10 +92,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var defaultLabels = []string{
|
var defaultLabels = []string{
|
||||||
"ubuntu-latest:docker://node:16-bullseye",
|
"ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest",
|
||||||
"ubuntu-22.04:docker://node:16-bullseye", // There's no node:16-bookworm yet
|
"ubuntu-24.04:docker://docker.gitea.com/runner-images:ubuntu-24.04",
|
||||||
"ubuntu-20.04:docker://node:16-bullseye",
|
"ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04",
|
||||||
"ubuntu-18.04:docker://node:16-buster",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type registerInputs struct {
|
type registerInputs struct {
|
||||||
@@ -102,6 +102,7 @@ type registerInputs struct {
|
|||||||
Token string
|
Token string
|
||||||
RunnerName string
|
RunnerName string
|
||||||
Labels []string
|
Labels []string
|
||||||
|
Ephemeral bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *registerInputs) validate() error {
|
func (r *registerInputs) validate() error {
|
||||||
@@ -126,6 +127,22 @@ func validateLabels(ls []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *registerInputs) stageValue(stage registerStage) string {
|
||||||
|
switch stage {
|
||||||
|
case StageInputInstance:
|
||||||
|
return r.InstanceAddr
|
||||||
|
case StageInputToken:
|
||||||
|
return r.Token
|
||||||
|
case StageInputRunnerName:
|
||||||
|
return r.RunnerName
|
||||||
|
case StageInputLabels:
|
||||||
|
if len(r.Labels) > 0 {
|
||||||
|
return strings.Join(r.Labels, ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (r *registerInputs) assignToNext(stage registerStage, value string, cfg *config.Config) registerStage {
|
func (r *registerInputs) assignToNext(stage registerStage, value string, cfg *config.Config) registerStage {
|
||||||
// must set instance address and token.
|
// must set instance address and token.
|
||||||
// if empty, keep current stage.
|
// if empty, keep current stage.
|
||||||
@@ -179,7 +196,8 @@ func (r *registerInputs) assignToNext(stage registerStage, value string, cfg *co
|
|||||||
}
|
}
|
||||||
|
|
||||||
if validateLabels(r.Labels) != nil {
|
if validateLabels(r.Labels) != nil {
|
||||||
log.Infoln("Invalid labels, please input again, leave blank to use the default labels (for example, ubuntu-20.04:docker://node:16-bullseye,ubuntu-18.04:docker://node:16-buster,linux_arm:host)")
|
log.Infoln("Invalid labels, please input again, leave blank to use the default labels (for example, ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest)")
|
||||||
|
r.Labels = nil
|
||||||
return StageInputLabels
|
return StageInputLabels
|
||||||
}
|
}
|
||||||
return StageWaitingForRegistration
|
return StageWaitingForRegistration
|
||||||
@@ -187,11 +205,25 @@ func (r *registerInputs) assignToNext(stage registerStage, value string, cfg *co
|
|||||||
return StageUnknown
|
return StageUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerInteractive(ctx context.Context, configFile string) error {
|
func initInputs(regArgs *registerArgs) *registerInputs {
|
||||||
|
inputs := ®isterInputs{
|
||||||
|
InstanceAddr: regArgs.InstanceAddr,
|
||||||
|
Token: regArgs.Token,
|
||||||
|
RunnerName: regArgs.RunnerName,
|
||||||
|
Ephemeral: regArgs.Ephemeral,
|
||||||
|
}
|
||||||
|
regArgs.Labels = strings.TrimSpace(regArgs.Labels)
|
||||||
|
// command line flag.
|
||||||
|
if regArgs.Labels != "" {
|
||||||
|
inputs.Labels = strings.Split(regArgs.Labels, ",")
|
||||||
|
}
|
||||||
|
return inputs
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerInteractive(ctx context.Context, configFile string, regArgs *registerArgs) error {
|
||||||
var (
|
var (
|
||||||
reader = bufio.NewReader(os.Stdin)
|
reader = bufio.NewReader(os.Stdin)
|
||||||
stage = StageInputInstance
|
stage = StageInputInstance
|
||||||
inputs = new(registerInputs)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cfg, err := config.LoadDefault(configFile)
|
cfg, err := config.LoadDefault(configFile)
|
||||||
@@ -201,13 +233,17 @@ func registerInteractive(ctx context.Context, configFile string) error {
|
|||||||
if f, err := os.Stat(cfg.Runner.File); err == nil && !f.IsDir() {
|
if f, err := os.Stat(cfg.Runner.File); err == nil && !f.IsDir() {
|
||||||
stage = StageOverwriteLocalConfig
|
stage = StageOverwriteLocalConfig
|
||||||
}
|
}
|
||||||
|
inputs := initInputs(regArgs)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
printStageHelp(stage)
|
cmdString := inputs.stageValue(stage)
|
||||||
|
if cmdString == "" {
|
||||||
cmdString, err := reader.ReadString('\n')
|
printStageHelp(stage)
|
||||||
if err != nil {
|
var err error
|
||||||
return err
|
cmdString, err = reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stage = inputs.assignToNext(stage, strings.TrimSpace(cmdString), cfg)
|
stage = inputs.assignToNext(stage, strings.TrimSpace(cmdString), cfg)
|
||||||
|
|
||||||
@@ -243,7 +279,7 @@ func printStageHelp(stage registerStage) {
|
|||||||
hostname, _ := os.Hostname()
|
hostname, _ := os.Hostname()
|
||||||
log.Infof("Enter the runner name (if set empty, use hostname: %s):\n", hostname)
|
log.Infof("Enter the runner name (if set empty, use hostname: %s):\n", hostname)
|
||||||
case StageInputLabels:
|
case StageInputLabels:
|
||||||
log.Infoln("Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-20.04:docker://node:16-bullseye,ubuntu-18.04:docker://node:16-buster,linux_arm:host):")
|
log.Infoln("Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest):")
|
||||||
case StageWaitingForRegistration:
|
case StageWaitingForRegistration:
|
||||||
log.Infoln("Waiting for registration...")
|
log.Infoln("Waiting for registration...")
|
||||||
}
|
}
|
||||||
@@ -254,17 +290,7 @@ func registerNoInteractive(ctx context.Context, configFile string, regArgs *regi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
inputs := ®isterInputs{
|
inputs := initInputs(regArgs)
|
||||||
InstanceAddr: regArgs.InstanceAddr,
|
|
||||||
Token: regArgs.Token,
|
|
||||||
RunnerName: regArgs.RunnerName,
|
|
||||||
Labels: defaultLabels,
|
|
||||||
}
|
|
||||||
regArgs.Labels = strings.TrimSpace(regArgs.Labels)
|
|
||||||
// command line flag.
|
|
||||||
if regArgs.Labels != "" {
|
|
||||||
inputs.Labels = strings.Split(regArgs.Labels, ",")
|
|
||||||
}
|
|
||||||
// specify labels in config file.
|
// specify labels in config file.
|
||||||
if len(cfg.Runner.Labels) > 0 {
|
if len(cfg.Runner.Labels) > 0 {
|
||||||
if regArgs.Labels != "" {
|
if regArgs.Labels != "" {
|
||||||
@@ -272,6 +298,9 @@ func registerNoInteractive(ctx context.Context, configFile string, regArgs *regi
|
|||||||
}
|
}
|
||||||
inputs.Labels = cfg.Runner.Labels
|
inputs.Labels = cfg.Runner.Labels
|
||||||
}
|
}
|
||||||
|
if len(inputs.Labels) == 0 {
|
||||||
|
inputs.Labels = defaultLabels
|
||||||
|
}
|
||||||
|
|
||||||
if inputs.RunnerName == "" {
|
if inputs.RunnerName == "" {
|
||||||
inputs.RunnerName, _ = os.Hostname()
|
inputs.RunnerName, _ = os.Hostname()
|
||||||
@@ -279,7 +308,7 @@ func registerNoInteractive(ctx context.Context, configFile string, regArgs *regi
|
|||||||
}
|
}
|
||||||
if err := inputs.validate(); err != nil {
|
if err := inputs.validate(); err != nil {
|
||||||
log.WithError(err).Errorf("Invalid input, please re-run act command.")
|
log.WithError(err).Errorf("Invalid input, please re-run act command.")
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
if err := doRegister(ctx, cfg, inputs); err != nil {
|
if err := doRegister(ctx, cfg, inputs); err != nil {
|
||||||
return fmt.Errorf("Failed to register runner: %w", err)
|
return fmt.Errorf("Failed to register runner: %w", err)
|
||||||
@@ -322,10 +351,11 @@ func doRegister(ctx context.Context, cfg *config.Config, inputs *registerInputs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
reg := &config.Registration{
|
reg := &config.Registration{
|
||||||
Name: inputs.RunnerName,
|
Name: inputs.RunnerName,
|
||||||
Token: inputs.Token,
|
Token: inputs.Token,
|
||||||
Address: inputs.InstanceAddr,
|
Address: inputs.InstanceAddr,
|
||||||
Labels: inputs.Labels,
|
Labels: inputs.Labels,
|
||||||
|
Ephemeral: inputs.Ephemeral,
|
||||||
}
|
}
|
||||||
|
|
||||||
ls := make([]string, len(reg.Labels))
|
ls := make([]string, len(reg.Labels))
|
||||||
@@ -340,6 +370,7 @@ func doRegister(ctx context.Context, cfg *config.Config, inputs *registerInputs)
|
|||||||
Version: ver.Version(),
|
Version: ver.Version(),
|
||||||
AgentLabels: ls, // Could be removed after Gitea 1.20
|
AgentLabels: ls, // Could be removed after Gitea 1.20
|
||||||
Labels: ls,
|
Labels: ls,
|
||||||
|
Ephemeral: reg.Ephemeral,
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("poller: cannot register new runner")
|
log.WithError(err).Error("poller: cannot register new runner")
|
||||||
@@ -351,6 +382,11 @@ func doRegister(ctx context.Context, cfg *config.Config, inputs *registerInputs)
|
|||||||
reg.Name = resp.Msg.Runner.Name
|
reg.Name = resp.Msg.Runner.Name
|
||||||
reg.Token = resp.Msg.Runner.Token
|
reg.Token = resp.Msg.Runner.Token
|
||||||
|
|
||||||
|
if inputs.Ephemeral != resp.Msg.Runner.Ephemeral {
|
||||||
|
// TODO we cannot remove the configuration via runner api, if we return an error here we just fill the database
|
||||||
|
log.Error("poller: cannot register new runner as ephemeral upgrade Gitea to gain security, run-once will be used automatically")
|
||||||
|
}
|
||||||
|
|
||||||
if err := config.SaveRegistration(cfg.Runner.File, reg); err != nil {
|
if err := config.SaveRegistration(cfg.Runner.File, reg); err != nil {
|
||||||
return fmt.Errorf("failed to save runner config: %w", err)
|
return fmt.Errorf("failed to save runner config: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
19
internal/app/cmd/register_test.go
Normal file
19
internal/app/cmd/register_test.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/v3/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegisterNonInteractiveReturnsLabelValidationError(t *testing.T) {
|
||||||
|
err := registerNoInteractive(t.Context(), "", ®isterArgs{
|
||||||
|
Labels: "label:invalid",
|
||||||
|
Token: "token",
|
||||||
|
InstanceAddr: "http://localhost:3000",
|
||||||
|
})
|
||||||
|
assert.Error(t, err, "unsupported schema: invalid")
|
||||||
|
}
|
||||||
@@ -18,47 +18,132 @@ import (
|
|||||||
"gitea.com/gitea/act_runner/internal/app/run"
|
"gitea.com/gitea/act_runner/internal/app/run"
|
||||||
"gitea.com/gitea/act_runner/internal/pkg/client"
|
"gitea.com/gitea/act_runner/internal/pkg/client"
|
||||||
"gitea.com/gitea/act_runner/internal/pkg/config"
|
"gitea.com/gitea/act_runner/internal/pkg/config"
|
||||||
|
"gitea.com/gitea/act_runner/internal/pkg/envcheck"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Poller struct {
|
type Poller struct {
|
||||||
client client.Client
|
client client.Client
|
||||||
runner *run.Runner
|
runner *run.Runner
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
tasksVersion atomic.Int64 // tasksVersion used to store the version of the last task fetched from the Gitea.
|
tasksVersion atomic.Int64 // tasksVersion used to store the version of the last task fetched from the Gitea.
|
||||||
|
bandwidthManager *envcheck.BandwidthManager
|
||||||
|
|
||||||
|
pollingCtx context.Context
|
||||||
|
shutdownPolling context.CancelFunc
|
||||||
|
|
||||||
|
jobsCtx context.Context
|
||||||
|
shutdownJobs context.CancelFunc
|
||||||
|
|
||||||
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *config.Config, client client.Client, runner *run.Runner) *Poller {
|
func New(cfg *config.Config, client client.Client, runner *run.Runner) *Poller {
|
||||||
|
pollingCtx, shutdownPolling := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
jobsCtx, shutdownJobs := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
return &Poller{
|
return &Poller{
|
||||||
client: client,
|
client: client,
|
||||||
runner: runner,
|
runner: runner,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
|
||||||
|
pollingCtx: pollingCtx,
|
||||||
|
shutdownPolling: shutdownPolling,
|
||||||
|
|
||||||
|
jobsCtx: jobsCtx,
|
||||||
|
shutdownJobs: shutdownJobs,
|
||||||
|
|
||||||
|
done: done,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Poller) Poll(ctx context.Context) {
|
// SetBandwidthManager sets the bandwidth manager for on-demand testing
|
||||||
|
func (p *Poller) SetBandwidthManager(bm *envcheck.BandwidthManager) {
|
||||||
|
p.bandwidthManager = bm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Poller) Poll() {
|
||||||
limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1)
|
limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1)
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
for i := 0; i < p.cfg.Runner.Capacity; i++ {
|
for i := 0; i < p.cfg.Runner.Capacity; i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go p.poll(ctx, wg, limiter)
|
go p.poll(wg, limiter)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
|
// signal that we shutdown
|
||||||
|
close(p.done)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Poller) poll(ctx context.Context, wg *sync.WaitGroup, limiter *rate.Limiter) {
|
func (p *Poller) PollOnce() {
|
||||||
|
limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1)
|
||||||
|
|
||||||
|
p.pollOnce(limiter)
|
||||||
|
|
||||||
|
// signal that we're done
|
||||||
|
close(p.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Poller) Shutdown(ctx context.Context) error {
|
||||||
|
p.shutdownPolling()
|
||||||
|
|
||||||
|
select {
|
||||||
|
// graceful shutdown completed succesfully
|
||||||
|
case <-p.done:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// our timeout for shutting down ran out
|
||||||
|
case <-ctx.Done():
|
||||||
|
// when both the timeout fires and the graceful shutdown
|
||||||
|
// completed succsfully, this branch of the select may
|
||||||
|
// fire. Do a non-blocking check here against the graceful
|
||||||
|
// shutdown status to avoid sending an error if we don't need to.
|
||||||
|
_, ok := <-p.done
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// force a shutdown of all running jobs
|
||||||
|
p.shutdownJobs()
|
||||||
|
|
||||||
|
// wait for running jobs to report their status to Gitea
|
||||||
|
_, _ = <-p.done
|
||||||
|
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Poller) poll(wg *sync.WaitGroup, limiter *rate.Limiter) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for {
|
for {
|
||||||
if err := limiter.Wait(ctx); err != nil {
|
p.pollOnce(limiter)
|
||||||
if ctx.Err() != nil {
|
|
||||||
|
select {
|
||||||
|
case <-p.pollingCtx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Poller) pollOnce(limiter *rate.Limiter) {
|
||||||
|
for {
|
||||||
|
if err := limiter.Wait(p.pollingCtx); err != nil {
|
||||||
|
if p.pollingCtx.Err() != nil {
|
||||||
log.WithError(err).Debug("limiter wait failed")
|
log.WithError(err).Debug("limiter wait failed")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
task, ok := p.fetchTask(ctx)
|
task, ok := p.fetchTask(p.pollingCtx)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p.runTaskWithRecover(ctx, task)
|
|
||||||
|
p.runTaskWithRecover(p.jobsCtx, task)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,11 +164,23 @@ func (p *Poller) fetchTask(ctx context.Context) (*runnerv1.Task, bool) {
|
|||||||
reqCtx, cancel := context.WithTimeout(ctx, p.cfg.Runner.FetchTimeout)
|
reqCtx, cancel := context.WithTimeout(ctx, p.cfg.Runner.FetchTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
// Detect capabilities including current disk space
|
||||||
|
caps := envcheck.DetectCapabilities(ctx, p.cfg.Container.DockerHost, p.cfg.Container.WorkdirParent)
|
||||||
|
|
||||||
|
// Include latest bandwidth result if available
|
||||||
|
if p.bandwidthManager != nil {
|
||||||
|
caps.Bandwidth = p.bandwidthManager.GetLastResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
capsJson := caps.ToJSON()
|
||||||
|
|
||||||
// Load the version value that was in the cache when the request was sent.
|
// Load the version value that was in the cache when the request was sent.
|
||||||
v := p.tasksVersion.Load()
|
v := p.tasksVersion.Load()
|
||||||
resp, err := p.client.FetchTask(reqCtx, connect.NewRequest(&runnerv1.FetchTaskRequest{
|
fetchReq := &runnerv1.FetchTaskRequest{
|
||||||
TasksVersion: v,
|
TasksVersion: v,
|
||||||
}))
|
CapabilitiesJson: capsJson,
|
||||||
|
}
|
||||||
|
resp, err := p.client.FetchTask(reqCtx, connect.NewRequest(fetchReq))
|
||||||
if errors.Is(err, context.DeadlineExceeded) {
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
@@ -96,6 +193,18 @@ func (p *Poller) fetchTask(ctx context.Context) (*runnerv1.Task, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if server requested a bandwidth test
|
||||||
|
if resp.Msg.RequestBandwidthTest && p.bandwidthManager != nil {
|
||||||
|
log.Info("Server requested bandwidth test, running now...")
|
||||||
|
go func() {
|
||||||
|
result := p.bandwidthManager.RunTest(ctx)
|
||||||
|
if result != nil {
|
||||||
|
log.Infof("Bandwidth test completed: %.1f Mbps download, %.0f ms latency",
|
||||||
|
result.DownloadMbps, result.Latency)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
if resp.Msg.TasksVersion > v {
|
if resp.Msg.TasksVersion > v {
|
||||||
p.tasksVersion.CompareAndSwap(v, resp.Msg.TasksVersion)
|
p.tasksVersion.CompareAndSwap(v, resp.Msg.TasksVersion)
|
||||||
}
|
}
|
||||||
@@ -104,7 +213,7 @@ func (p *Poller) fetchTask(ctx context.Context) (*runnerv1.Task, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// got a task, set `tasksVersion` to zero to focre query db in next request.
|
// got a task, set tasksVersion to zero to force query db in next request.
|
||||||
p.tasksVersion.CompareAndSwap(resp.Msg.TasksVersion, 0)
|
p.tasksVersion.CompareAndSwap(resp.Msg.TasksVersion, 0)
|
||||||
|
|
||||||
return resp.Msg.Task, true
|
return resp.Msg.Task, true
|
||||||
|
|||||||
24
internal/app/run/logging.go
Normal file
24
internal/app/run/logging.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package run
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NullLogger is used to create a new JobLogger to discard logs. This
|
||||||
|
// will prevent these logs from being logged to the stdout, but
|
||||||
|
// forward them to the Reporter via its hook.
|
||||||
|
type NullLogger struct{}
|
||||||
|
|
||||||
|
// WithJobLogger creates a new logrus.Logger that will discard all logs.
|
||||||
|
func (n NullLogger) WithJobLogger() *log.Logger {
|
||||||
|
logger := log.New()
|
||||||
|
logger.SetOutput(io.Discard)
|
||||||
|
logger.SetLevel(log.TraceLevel)
|
||||||
|
|
||||||
|
return logger
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -41,6 +42,48 @@ type Runner struct {
|
|||||||
runningTasks sync.Map
|
runningTasks sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getJobCacheDir returns a job-isolated cache directory
|
||||||
|
func (r *Runner) getJobCacheDir(taskID int64) string {
|
||||||
|
return filepath.Join(r.cfg.Host.WorkdirParent, "jobs", fmt.Sprintf("%d", taskID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanupJobCache removes the job-specific cache directory after completion
|
||||||
|
func (r *Runner) cleanupJobCache(taskID int64) {
|
||||||
|
jobCacheDir := r.getJobCacheDir(taskID)
|
||||||
|
if err := os.RemoveAll(jobCacheDir); err != nil {
|
||||||
|
log.Warnf("failed to cleanup job cache %s: %v", jobCacheDir, err)
|
||||||
|
} else {
|
||||||
|
log.Infof("cleaned up job cache: %s", jobCacheDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanStaleJobCaches removes job cache directories older than maxAge
|
||||||
|
func (r *Runner) CleanStaleJobCaches(maxAge time.Duration) {
|
||||||
|
jobsDir := filepath.Join(r.cfg.Host.WorkdirParent, "jobs")
|
||||||
|
entries, err := os.ReadDir(jobsDir)
|
||||||
|
if err != nil {
|
||||||
|
return // directory may not exist yet
|
||||||
|
}
|
||||||
|
|
||||||
|
cutoff := time.Now().Add(-maxAge)
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
info, err := entry.Info()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if info.ModTime().Before(cutoff) {
|
||||||
|
jobPath := filepath.Join(jobsDir, entry.Name())
|
||||||
|
if err := os.RemoveAll(jobPath); err != nil {
|
||||||
|
log.Warnf("failed to remove stale job cache %s: %v", jobPath, err)
|
||||||
|
} else {
|
||||||
|
log.Infof("evicted stale job cache: %s", jobPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
func NewRunner(cfg *config.Config, reg *config.Registration, cli client.Client) *Runner {
|
func NewRunner(cfg *config.Config, reg *config.Registration, cli client.Client) *Runner {
|
||||||
ls := labels.Labels{}
|
ls := labels.Labels{}
|
||||||
for _, v := range reg.Labels {
|
for _, v := range reg.Labels {
|
||||||
@@ -95,6 +138,7 @@ func (r *Runner) Run(ctx context.Context, task *runnerv1.Task) error {
|
|||||||
}
|
}
|
||||||
r.runningTasks.Store(task.Id, struct{}{})
|
r.runningTasks.Store(task.Id, struct{}{})
|
||||||
defer r.runningTasks.Delete(task.Id)
|
defer r.runningTasks.Delete(task.Id)
|
||||||
|
defer r.cleanupJobCache(task.Id)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, r.cfg.Runner.Timeout)
|
ctx, cancel := context.WithTimeout(ctx, r.cfg.Runner.Timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -113,6 +157,17 @@ func (r *Runner) Run(ctx context.Context, task *runnerv1.Task) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getDefaultActionsURL
|
||||||
|
// when DEFAULT_ACTIONS_URL == "https://github.com" and GithubMirror is not blank,
|
||||||
|
// it should be set to GithubMirror first.
|
||||||
|
func (r *Runner) getDefaultActionsURL(ctx context.Context, task *runnerv1.Task) string {
|
||||||
|
giteaDefaultActionsURL := task.Context.Fields["gitea_default_actions_url"].GetStringValue()
|
||||||
|
if giteaDefaultActionsURL == "https://github.com" && r.cfg.Runner.GithubMirror != "" {
|
||||||
|
return r.cfg.Runner.GithubMirror
|
||||||
|
}
|
||||||
|
return giteaDefaultActionsURL
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.Reporter) (err error) {
|
func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.Reporter) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -137,7 +192,7 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
|||||||
taskContext := task.Context.Fields
|
taskContext := task.Context.Fields
|
||||||
|
|
||||||
log.Infof("task %v repo is %v %v %v", task.Id, taskContext["repository"].GetStringValue(),
|
log.Infof("task %v repo is %v %v %v", task.Id, taskContext["repository"].GetStringValue(),
|
||||||
taskContext["gitea_default_actions_url"].GetStringValue(),
|
r.getDefaultActionsURL(ctx, task),
|
||||||
r.client.Address())
|
r.client.Address())
|
||||||
|
|
||||||
preset := &model.GithubContext{
|
preset := &model.GithubContext{
|
||||||
@@ -163,6 +218,12 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
|||||||
preset.Token = t
|
preset.Token = t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if actionsIdTokenRequestUrl := taskContext["actions_id_token_request_url"].GetStringValue(); actionsIdTokenRequestUrl != "" {
|
||||||
|
r.envs["ACTIONS_ID_TOKEN_REQUEST_URL"] = actionsIdTokenRequestUrl
|
||||||
|
r.envs["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = taskContext["actions_id_token_request_token"].GetStringValue()
|
||||||
|
task.Secrets["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = r.envs["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]
|
||||||
|
}
|
||||||
|
|
||||||
giteaRuntimeToken := taskContext["gitea_runtime_token"].GetStringValue()
|
giteaRuntimeToken := taskContext["gitea_runtime_token"].GetStringValue()
|
||||||
if giteaRuntimeToken == "" {
|
if giteaRuntimeToken == "" {
|
||||||
// use task token to action api token for previous Gitea Server Versions
|
// use task token to action api token for previous Gitea Server Versions
|
||||||
@@ -180,19 +241,30 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
|||||||
maxLifetime = time.Until(deadline)
|
maxLifetime = time.Until(deadline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create job-specific environment with isolated cache directories
|
||||||
|
jobCacheDir := r.getJobCacheDir(task.Id)
|
||||||
|
jobEnvs := make(map[string]string, len(r.envs)+2)
|
||||||
|
for k, v := range r.envs {
|
||||||
|
jobEnvs[k] = v
|
||||||
|
}
|
||||||
|
// Isolate golangci-lint cache to prevent parallel job conflicts
|
||||||
|
jobEnvs["GOLANGCI_LINT_CACHE"] = filepath.Join(jobCacheDir, "golangci-lint")
|
||||||
|
// Set XDG_CACHE_HOME to isolate other tools that respect it
|
||||||
|
jobEnvs["XDG_CACHE_HOME"] = jobCacheDir
|
||||||
|
|
||||||
runnerConfig := &runner.Config{
|
runnerConfig := &runner.Config{
|
||||||
// On Linux, Workdir will be like "/<parent_directory>/<owner>/<repo>"
|
// On Linux, Workdir will be like "/<parent_directory>/<owner>/<repo>"
|
||||||
// On Windows, Workdir will be like "\<parent_directory>\<owner>\<repo>"
|
// On Windows, Workdir will be like "\<parent_directory>\<owner>\<repo>"
|
||||||
Workdir: filepath.FromSlash(fmt.Sprintf("/%s/%s", strings.TrimLeft(r.cfg.Container.WorkdirParent, "/"), preset.Repository)),
|
Workdir: filepath.FromSlash(fmt.Sprintf("/%s/%s", strings.TrimLeft(r.cfg.Container.WorkdirParent, "/"), preset.Repository)),
|
||||||
BindWorkdir: false,
|
BindWorkdir: false,
|
||||||
ActionCacheDir: filepath.FromSlash(r.cfg.Host.WorkdirParent),
|
ActionCacheDir: filepath.FromSlash(jobCacheDir),
|
||||||
|
|
||||||
ReuseContainers: false,
|
ReuseContainers: false,
|
||||||
ForcePull: r.cfg.Container.ForcePull,
|
ForcePull: r.cfg.Container.ForcePull,
|
||||||
ForceRebuild: r.cfg.Container.ForceRebuild,
|
ForceRebuild: r.cfg.Container.ForceRebuild,
|
||||||
LogOutput: true,
|
LogOutput: true,
|
||||||
JSONLogger: false,
|
JSONLogger: false,
|
||||||
Env: r.envs,
|
Env: jobEnvs,
|
||||||
Secrets: task.Secrets,
|
Secrets: task.Secrets,
|
||||||
GitHubInstance: strings.TrimSuffix(r.client.Address(), "/"),
|
GitHubInstance: strings.TrimSuffix(r.client.Address(), "/"),
|
||||||
AutoRemove: true,
|
AutoRemove: true,
|
||||||
@@ -205,7 +277,7 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
|||||||
ContainerOptions: r.cfg.Container.Options,
|
ContainerOptions: r.cfg.Container.Options,
|
||||||
ContainerDaemonSocket: r.cfg.Container.DockerHost,
|
ContainerDaemonSocket: r.cfg.Container.DockerHost,
|
||||||
Privileged: r.cfg.Container.Privileged,
|
Privileged: r.cfg.Container.Privileged,
|
||||||
DefaultActionInstance: taskContext["gitea_default_actions_url"].GetStringValue(),
|
DefaultActionInstance: r.getDefaultActionsURL(ctx, task),
|
||||||
PlatformPicker: r.labels.PickPlatform,
|
PlatformPicker: r.labels.PickPlatform,
|
||||||
Vars: task.Vars,
|
Vars: task.Vars,
|
||||||
ValidVolumes: r.cfg.Container.ValidVolumes,
|
ValidVolumes: r.cfg.Container.ValidVolumes,
|
||||||
@@ -223,14 +295,19 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
|||||||
// add logger recorders
|
// add logger recorders
|
||||||
ctx = common.WithLoggerHook(ctx, reporter)
|
ctx = common.WithLoggerHook(ctx, reporter)
|
||||||
|
|
||||||
|
if !log.IsLevelEnabled(log.DebugLevel) {
|
||||||
|
ctx = runner.WithJobLoggerFactory(ctx, NullLogger{})
|
||||||
|
}
|
||||||
|
|
||||||
execErr := executor(ctx)
|
execErr := executor(ctx)
|
||||||
reporter.SetOutputs(job.Outputs)
|
reporter.SetOutputs(job.Outputs)
|
||||||
return execErr
|
return execErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) Declare(ctx context.Context, labels []string) (*connect.Response[runnerv1.DeclareResponse], error) {
|
func (r *Runner) Declare(ctx context.Context, labels []string, capabilitiesJson string) (*connect.Response[runnerv1.DeclareResponse], error) {
|
||||||
return r.client.Declare(ctx, connect.NewRequest(&runnerv1.DeclareRequest{
|
return r.client.Declare(ctx, connect.NewRequest(&runnerv1.DeclareRequest{
|
||||||
Version: ver.Version(),
|
Version: ver.Version(),
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
|
CapabilitiesJson: capabilitiesJson,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,19 +21,31 @@ runner:
|
|||||||
env_file: .env
|
env_file: .env
|
||||||
# The timeout for a job to be finished.
|
# The timeout for a job to be finished.
|
||||||
# Please note that the Gitea instance also has a timeout (3h by default) for the job.
|
# Please note that the Gitea instance also has a timeout (3h by default) for the job.
|
||||||
# So the job could be stopped by the Gitea instance if it's timeout is shorter than this.
|
# So the job could be stopped by the Gitea instance if its timeout is shorter than this.
|
||||||
timeout: 3h
|
timeout: 3h
|
||||||
|
# The timeout for the runner to wait for running jobs to finish when shutting down.
|
||||||
|
# Any running jobs that haven't finished after this timeout will be cancelled.
|
||||||
|
shutdown_timeout: 0s
|
||||||
# Whether skip verifying the TLS certificate of the Gitea instance.
|
# Whether skip verifying the TLS certificate of the Gitea instance.
|
||||||
insecure: false
|
insecure: false
|
||||||
# The timeout for fetching the job from the Gitea instance.
|
# The timeout for fetching the job from the Gitea instance.
|
||||||
fetch_timeout: 5s
|
fetch_timeout: 5s
|
||||||
# The interval for fetching the job from the Gitea instance.
|
# The interval for fetching the job from the Gitea instance.
|
||||||
fetch_interval: 2s
|
fetch_interval: 2s
|
||||||
|
# The github_mirror of a runner is used to specify the mirror address of the github that pulls the action repository.
|
||||||
|
# It works when something like `uses: actions/checkout@v4` is used and DEFAULT_ACTIONS_URL is set to github,
|
||||||
|
# and github_mirror is not empty. In this case,
|
||||||
|
# it replaces https://github.com with the value here, which is useful for some special network environments.
|
||||||
|
github_mirror: ''
|
||||||
# The labels of a runner are used to determine which jobs the runner can run, and how to run them.
|
# The labels of a runner are used to determine which jobs the runner can run, and how to run them.
|
||||||
# Like: ["macos-arm64:host", "ubuntu-latest:docker://node:16-bullseye", "ubuntu-22.04:docker://node:16-bullseye"]
|
# Like: "macos-arm64:host" or "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
|
||||||
|
# Find more images provided by Gitea at https://gitea.com/gitea/runner-images .
|
||||||
# If it's empty when registering, it will ask for inputting labels.
|
# If it's empty when registering, it will ask for inputting labels.
|
||||||
# If it's empty when execute `deamon`, will use labels in `.runner` file.
|
# If it's empty when execute `daemon`, will use labels in `.runner` file.
|
||||||
labels: []
|
labels:
|
||||||
|
- "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
|
||||||
|
- "ubuntu-24.04:docker://docker.gitea.com/runner-images:ubuntu-24.04"
|
||||||
|
- "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04"
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
# Enable cache server to use actions/cache.
|
# Enable cache server to use actions/cache.
|
||||||
@@ -60,7 +72,7 @@ container:
|
|||||||
network: ""
|
network: ""
|
||||||
# Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker).
|
# Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker).
|
||||||
privileged: false
|
privileged: false
|
||||||
# And other options to be used when the container is started (eg, --add-host=my.gitea.url:host-gateway).
|
# Any other options to be used when the container is started (e.g., --add-host=my.gitea.url:host-gateway).
|
||||||
options:
|
options:
|
||||||
# The parent directory of a job's working directory.
|
# The parent directory of a job's working directory.
|
||||||
# NOTE: There is no need to add the first '/' of the path as act_runner will add it automatically.
|
# NOTE: There is no need to add the first '/' of the path as act_runner will add it automatically.
|
||||||
@@ -78,15 +90,19 @@ container:
|
|||||||
# valid_volumes:
|
# valid_volumes:
|
||||||
# - '**'
|
# - '**'
|
||||||
valid_volumes: []
|
valid_volumes: []
|
||||||
# overrides the docker client host with the specified one.
|
# Overrides the docker client host with the specified one.
|
||||||
# If it's empty, act_runner will find an available docker host automatically.
|
# If it's empty, act_runner will find an available docker host automatically.
|
||||||
# If it's "-", act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers.
|
# If it's "-", act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers.
|
||||||
# If it's not empty or "-", the specified docker host will be used. An error will be returned if it doesn't work.
|
# If it's not empty or "-", the specified docker host will be used. An error will be returned if it doesn't work.
|
||||||
docker_host: ""
|
docker_host: ""
|
||||||
# Pull docker image(s) even if already present
|
# Pull docker image(s) even if already present
|
||||||
force_pull: false
|
force_pull: true
|
||||||
# Rebuild docker image(s) even if already present
|
# Rebuild docker image(s) even if already present
|
||||||
force_rebuild: false
|
force_rebuild: false
|
||||||
|
# Always require a reachable docker daemon, even if not required by act_runner
|
||||||
|
require_docker: false
|
||||||
|
# Timeout to wait for the docker daemon to be reachable, if docker is required by require_docker or act_runner
|
||||||
|
docker_timeout: 0s
|
||||||
|
|
||||||
host:
|
host:
|
||||||
# The parent directory of a job's working directory.
|
# The parent directory of a job's working directory.
|
||||||
|
|||||||
@@ -21,15 +21,17 @@ type Log struct {
|
|||||||
|
|
||||||
// Runner represents the configuration for the runner.
|
// Runner represents the configuration for the runner.
|
||||||
type Runner struct {
|
type Runner struct {
|
||||||
File string `yaml:"file"` // File specifies the file path for the runner.
|
File string `yaml:"file"` // File specifies the file path for the runner.
|
||||||
Capacity int `yaml:"capacity"` // Capacity specifies the capacity of the runner.
|
Capacity int `yaml:"capacity"` // Capacity specifies the capacity of the runner.
|
||||||
Envs map[string]string `yaml:"envs"` // Envs stores environment variables for the runner.
|
Envs map[string]string `yaml:"envs"` // Envs stores environment variables for the runner.
|
||||||
EnvFile string `yaml:"env_file"` // EnvFile specifies the path to the file containing environment variables for the runner.
|
EnvFile string `yaml:"env_file"` // EnvFile specifies the path to the file containing environment variables for the runner.
|
||||||
Timeout time.Duration `yaml:"timeout"` // Timeout specifies the duration for runner timeout.
|
Timeout time.Duration `yaml:"timeout"` // Timeout specifies the duration for runner timeout.
|
||||||
Insecure bool `yaml:"insecure"` // Insecure indicates whether the runner operates in an insecure mode.
|
ShutdownTimeout time.Duration `yaml:"shutdown_timeout"` // ShutdownTimeout specifies the duration to wait for running jobs to complete during a shutdown of the runner.
|
||||||
FetchTimeout time.Duration `yaml:"fetch_timeout"` // FetchTimeout specifies the timeout duration for fetching resources.
|
Insecure bool `yaml:"insecure"` // Insecure indicates whether the runner operates in an insecure mode.
|
||||||
FetchInterval time.Duration `yaml:"fetch_interval"` // FetchInterval specifies the interval duration for fetching resources.
|
FetchTimeout time.Duration `yaml:"fetch_timeout"` // FetchTimeout specifies the timeout duration for fetching resources.
|
||||||
Labels []string `yaml:"labels"` // Labels specifies the labels of the runner. Labels are declared on each startup
|
FetchInterval time.Duration `yaml:"fetch_interval"` // FetchInterval specifies the interval duration for fetching resources.
|
||||||
|
Labels []string `yaml:"labels"` // Labels specify the labels of the runner. Labels are declared on each startup
|
||||||
|
GithubMirror string `yaml:"github_mirror"` // GithubMirror defines what mirrors should be used when using github
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache represents the configuration for caching.
|
// Cache represents the configuration for caching.
|
||||||
@@ -43,15 +45,17 @@ type Cache struct {
|
|||||||
|
|
||||||
// Container represents the configuration for the container.
|
// Container represents the configuration for the container.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
Network string `yaml:"network"` // Network specifies the network for the container.
|
Network string `yaml:"network"` // Network specifies the network for the container.
|
||||||
NetworkMode string `yaml:"network_mode"` // Deprecated: use Network instead. Could be removed after Gitea 1.20
|
NetworkMode string `yaml:"network_mode"` // Deprecated: use Network instead. Could be removed after Gitea 1.20
|
||||||
Privileged bool `yaml:"privileged"` // Privileged indicates whether the container runs in privileged mode.
|
Privileged bool `yaml:"privileged"` // Privileged indicates whether the container runs in privileged mode.
|
||||||
Options string `yaml:"options"` // Options specifies additional options for the container.
|
Options string `yaml:"options"` // Options specifies additional options for the container.
|
||||||
WorkdirParent string `yaml:"workdir_parent"` // WorkdirParent specifies the parent directory for the container's working directory.
|
WorkdirParent string `yaml:"workdir_parent"` // WorkdirParent specifies the parent directory for the container's working directory.
|
||||||
ValidVolumes []string `yaml:"valid_volumes"` // ValidVolumes specifies the volumes (including bind mounts) can be mounted to containers.
|
ValidVolumes []string `yaml:"valid_volumes"` // ValidVolumes specifies the volumes (including bind mounts) can be mounted to containers.
|
||||||
DockerHost string `yaml:"docker_host"` // DockerHost specifies the Docker host. It overrides the value specified in environment variable DOCKER_HOST.
|
DockerHost string `yaml:"docker_host"` // DockerHost specifies the Docker host. It overrides the value specified in environment variable DOCKER_HOST.
|
||||||
ForcePull bool `yaml:"force_pull"` // Pull docker image(s) even if already present
|
ForcePull bool `yaml:"force_pull"` // Pull docker image(s) even if already present
|
||||||
ForceRebuild bool `yaml:"force_rebuild"` // Rebuild docker image(s) even if already present
|
ForceRebuild bool `yaml:"force_rebuild"` // Rebuild docker image(s) even if already present
|
||||||
|
RequireDocker bool `yaml:"require_docker"` // Always require a reachable docker daemon, even if not required by act_runner
|
||||||
|
DockerTimeout time.Duration `yaml:"docker_timeout"` // Timeout to wait for the docker daemon to be reachable, if docker is required by require_docker or act_runner
|
||||||
}
|
}
|
||||||
|
|
||||||
// Host represents the configuration for the host.
|
// Host represents the configuration for the host.
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ const registrationWarning = "This file is automatically generated by act-runner.
|
|||||||
type Registration struct {
|
type Registration struct {
|
||||||
Warning string `json:"WARNING"` // Warning message to display, it's always the registrationWarning constant
|
Warning string `json:"WARNING"` // Warning message to display, it's always the registrationWarning constant
|
||||||
|
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
UUID string `json:"uuid"`
|
UUID string `json:"uuid"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Labels []string `json:"labels"`
|
Labels []string `json:"labels"`
|
||||||
|
Ephemeral bool `json:"ephemeral"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadRegistration(file string) (*Registration, error) {
|
func LoadRegistration(file string) (*Registration, error) {
|
||||||
|
|||||||
209
internal/pkg/envcheck/bandwidth.go
Normal file
209
internal/pkg/envcheck/bandwidth.go
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package envcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BandwidthInfo holds network bandwidth test results
|
||||||
|
type BandwidthInfo struct {
|
||||||
|
DownloadMbps float64 `json:"download_mbps"`
|
||||||
|
UploadMbps float64 `json:"upload_mbps,omitempty"`
|
||||||
|
Latency float64 `json:"latency_ms,omitempty"`
|
||||||
|
TestedAt time.Time `json:"tested_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BandwidthManager handles periodic bandwidth testing
|
||||||
|
type BandwidthManager struct {
|
||||||
|
serverURL string
|
||||||
|
lastResult *BandwidthInfo
|
||||||
|
mu sync.RWMutex
|
||||||
|
testInterval time.Duration
|
||||||
|
stopChan chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBandwidthManager creates a new bandwidth manager
|
||||||
|
func NewBandwidthManager(serverURL string, testInterval time.Duration) *BandwidthManager {
|
||||||
|
return &BandwidthManager{
|
||||||
|
serverURL: serverURL,
|
||||||
|
testInterval: testInterval,
|
||||||
|
stopChan: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start begins periodic bandwidth testing
|
||||||
|
func (bm *BandwidthManager) Start(ctx context.Context) {
|
||||||
|
// Run initial test
|
||||||
|
bm.RunTest(ctx)
|
||||||
|
|
||||||
|
// Start periodic testing
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(bm.testInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
bm.RunTest(ctx)
|
||||||
|
case <-bm.stopChan:
|
||||||
|
return
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the periodic testing
|
||||||
|
func (bm *BandwidthManager) Stop() {
|
||||||
|
close(bm.stopChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunTest runs a bandwidth test and stores the result
|
||||||
|
func (bm *BandwidthManager) RunTest(ctx context.Context) *BandwidthInfo {
|
||||||
|
result := TestBandwidth(ctx, bm.serverURL)
|
||||||
|
|
||||||
|
bm.mu.Lock()
|
||||||
|
bm.lastResult = result
|
||||||
|
bm.mu.Unlock()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastResult returns the most recent bandwidth test result
|
||||||
|
func (bm *BandwidthManager) GetLastResult() *BandwidthInfo {
|
||||||
|
bm.mu.RLock()
|
||||||
|
defer bm.mu.RUnlock()
|
||||||
|
return bm.lastResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBandwidth tests network bandwidth to the Gitea server
|
||||||
|
func TestBandwidth(ctx context.Context, serverURL string) *BandwidthInfo {
|
||||||
|
if serverURL == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
info := &BandwidthInfo{
|
||||||
|
TestedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test latency first
|
||||||
|
info.Latency = testLatency(ctx, serverURL)
|
||||||
|
|
||||||
|
// Test download speed
|
||||||
|
info.DownloadMbps = testDownloadSpeed(ctx, serverURL)
|
||||||
|
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLatency(ctx context.Context, serverURL string) float64 {
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
reqCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(reqCtx, "HEAD", serverURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
latency := time.Since(start).Seconds() * 1000 // Convert to ms
|
||||||
|
return float64(int(latency*100)) / 100 // Round to 2 decimals
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDownloadSpeed(ctx context.Context, serverURL string) float64 {
|
||||||
|
// Try multiple endpoints to accumulate ~1MB of data
|
||||||
|
endpoints := []string{
|
||||||
|
"/assets/css/index.css",
|
||||||
|
"/assets/js/index.js",
|
||||||
|
"/assets/img/logo.svg",
|
||||||
|
"/assets/img/logo.png",
|
||||||
|
"/",
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalBytes int64
|
||||||
|
var totalDuration time.Duration
|
||||||
|
targetBytes := int64(1024 * 1024) // 1MB target
|
||||||
|
maxAttempts := 10 // Limit iterations
|
||||||
|
|
||||||
|
for attempt := 0; attempt < maxAttempts && totalBytes < targetBytes; attempt++ {
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
if totalBytes >= targetBytes {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
url := serverURL + endpoint
|
||||||
|
|
||||||
|
reqCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
req, err := http.NewRequestWithContext(reqCtx, "GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
n, _ := io.Copy(io.Discard, resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
totalBytes += n
|
||||||
|
totalDuration += duration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalBytes == 0 || totalDuration == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate speed in Mbps
|
||||||
|
seconds := totalDuration.Seconds()
|
||||||
|
if seconds == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesPerSecond := float64(totalBytes) / seconds
|
||||||
|
mbps := (bytesPerSecond * 8) / (1024 * 1024)
|
||||||
|
|
||||||
|
return float64(int(mbps*100)) / 100
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatBandwidth formats bandwidth for display
|
||||||
|
func FormatBandwidth(mbps float64) string {
|
||||||
|
if mbps == 0 {
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
if mbps >= 1000 {
|
||||||
|
return fmt.Sprintf("%.1f Gbps", mbps/1000)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.1f Mbps", mbps)
|
||||||
|
}
|
||||||
889
internal/pkg/envcheck/capabilities.go
Normal file
889
internal/pkg/envcheck/capabilities.go
Normal file
@@ -0,0 +1,889 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package envcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DiskInfo holds disk space information
|
||||||
|
type DiskInfo struct {
|
||||||
|
Path string `json:"path,omitempty"` // Path being checked (working directory)
|
||||||
|
Total uint64 `json:"total_bytes"`
|
||||||
|
Free uint64 `json:"free_bytes"`
|
||||||
|
Used uint64 `json:"used_bytes"`
|
||||||
|
UsedPercent float64 `json:"used_percent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DistroInfo holds Linux distribution information
|
||||||
|
type DistroInfo struct {
|
||||||
|
ID string `json:"id,omitempty"` // e.g., "ubuntu", "debian", "fedora"
|
||||||
|
VersionID string `json:"version_id,omitempty"` // e.g., "24.04", "12"
|
||||||
|
PrettyName string `json:"pretty_name,omitempty"` // e.g., "Ubuntu 24.04 LTS"
|
||||||
|
}
|
||||||
|
|
||||||
|
// XcodeInfo holds Xcode and iOS development information
|
||||||
|
type XcodeInfo struct {
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
Build string `json:"build,omitempty"`
|
||||||
|
SDKs []string `json:"sdks,omitempty"` // e.g., ["iOS 17.0", "macOS 14.0"]
|
||||||
|
Simulators []string `json:"simulators,omitempty"` // Available iOS simulators
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunnerCapabilities represents the capabilities of a runner for AI consumption
|
||||||
|
type RunnerCapabilities struct {
|
||||||
|
OS string `json:"os"`
|
||||||
|
Arch string `json:"arch"`
|
||||||
|
Distro *DistroInfo `json:"distro,omitempty"`
|
||||||
|
Xcode *XcodeInfo `json:"xcode,omitempty"`
|
||||||
|
Docker bool `json:"docker"`
|
||||||
|
DockerCompose bool `json:"docker_compose"`
|
||||||
|
ContainerRuntime string `json:"container_runtime,omitempty"`
|
||||||
|
Shell []string `json:"shell,omitempty"`
|
||||||
|
Tools map[string][]string `json:"tools,omitempty"`
|
||||||
|
BuildTools []string `json:"build_tools,omitempty"` // Available build/installer tools
|
||||||
|
PackageManagers []string `json:"package_managers,omitempty"`
|
||||||
|
Features *CapabilityFeatures `json:"features,omitempty"`
|
||||||
|
Limitations []string `json:"limitations,omitempty"`
|
||||||
|
Disk *DiskInfo `json:"disk,omitempty"`
|
||||||
|
Bandwidth *BandwidthInfo `json:"bandwidth,omitempty"`
|
||||||
|
SuggestedLabels []string `json:"suggested_labels,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CapabilityFeatures represents feature support flags
|
||||||
|
type CapabilityFeatures struct {
|
||||||
|
ArtifactsV4 bool `json:"artifacts_v4"`
|
||||||
|
Cache bool `json:"cache"`
|
||||||
|
Services bool `json:"services"`
|
||||||
|
CompositeActions bool `json:"composite_actions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectCapabilities detects the runner's capabilities
|
||||||
|
// workingDir is the directory where builds will run (for disk space detection)
|
||||||
|
func DetectCapabilities(ctx context.Context, dockerHost string, workingDir string) *RunnerCapabilities {
|
||||||
|
cap := &RunnerCapabilities{
|
||||||
|
OS: runtime.GOOS,
|
||||||
|
Arch: runtime.GOARCH,
|
||||||
|
Tools: make(map[string][]string),
|
||||||
|
BuildTools: []string{},
|
||||||
|
PackageManagers: []string{},
|
||||||
|
Shell: detectShells(),
|
||||||
|
Features: &CapabilityFeatures{
|
||||||
|
ArtifactsV4: false, // Gitea doesn't support v4 artifacts
|
||||||
|
Cache: true,
|
||||||
|
Services: true,
|
||||||
|
CompositeActions: true,
|
||||||
|
},
|
||||||
|
Limitations: []string{
|
||||||
|
"actions/upload-artifact@v4 not supported (use v3 or direct API upload)",
|
||||||
|
"actions/download-artifact@v4 not supported (use v3)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect Linux distribution
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
cap.Distro = detectLinuxDistro()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect macOS Xcode/iOS
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
cap.Xcode = detectXcode(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect Docker
|
||||||
|
cap.Docker, cap.ContainerRuntime = detectDocker(ctx, dockerHost)
|
||||||
|
if cap.Docker {
|
||||||
|
cap.DockerCompose = detectDockerCompose(ctx)
|
||||||
|
cap.Features.Services = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect common tools
|
||||||
|
detectTools(ctx, cap)
|
||||||
|
|
||||||
|
// Detect build tools
|
||||||
|
detectBuildTools(ctx, cap)
|
||||||
|
|
||||||
|
// Detect package managers
|
||||||
|
detectPackageManagers(ctx, cap)
|
||||||
|
|
||||||
|
// Detect disk space on the working directory's filesystem
|
||||||
|
cap.Disk = detectDiskSpace(workingDir)
|
||||||
|
|
||||||
|
// Generate suggested labels based on detected capabilities
|
||||||
|
cap.SuggestedLabels = generateSuggestedLabels(cap)
|
||||||
|
|
||||||
|
return cap
|
||||||
|
}
|
||||||
|
|
||||||
|
// detectXcode detects Xcode and iOS development capabilities on macOS
|
||||||
|
func detectXcode(ctx context.Context) *XcodeInfo {
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Check for xcodebuild
|
||||||
|
cmd := exec.CommandContext(timeoutCtx, "xcodebuild", "-version")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
xcode := &XcodeInfo{}
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.HasPrefix(line, "Xcode ") {
|
||||||
|
xcode.Version = strings.TrimPrefix(line, "Xcode ")
|
||||||
|
} else if strings.HasPrefix(line, "Build version ") {
|
||||||
|
xcode.Build = strings.TrimPrefix(line, "Build version ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get available SDKs
|
||||||
|
cmd = exec.CommandContext(timeoutCtx, "xcodebuild", "-showsdks")
|
||||||
|
output, err = cmd.Output()
|
||||||
|
if err == nil {
|
||||||
|
lines = strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
// Look for SDK lines like "-sdk iphoneos17.0" or "iOS 17.0"
|
||||||
|
if strings.Contains(line, "SDK") || strings.HasPrefix(line, "-sdk") {
|
||||||
|
continue // Skip header lines
|
||||||
|
}
|
||||||
|
if strings.Contains(line, "iOS") || strings.Contains(line, "macOS") ||
|
||||||
|
strings.Contains(line, "watchOS") || strings.Contains(line, "tvOS") ||
|
||||||
|
strings.Contains(line, "visionOS") || strings.Contains(line, "xrOS") {
|
||||||
|
// Extract SDK name
|
||||||
|
if idx := strings.Index(line, "-sdk"); idx != -1 {
|
||||||
|
sdkPart := strings.TrimSpace(line[:idx])
|
||||||
|
if sdkPart != "" {
|
||||||
|
xcode.SDKs = append(xcode.SDKs, sdkPart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get available simulators
|
||||||
|
cmd = exec.CommandContext(timeoutCtx, "xcrun", "simctl", "list", "devices", "available", "-j")
|
||||||
|
output, err = cmd.Output()
|
||||||
|
if err == nil {
|
||||||
|
var simData struct {
|
||||||
|
Devices map[string][]struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
State string `json:"state"`
|
||||||
|
} `json:"devices"`
|
||||||
|
}
|
||||||
|
if json.Unmarshal(output, &simData) == nil {
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
for runtime, devices := range simData.Devices {
|
||||||
|
if strings.Contains(runtime, "iOS") {
|
||||||
|
for _, dev := range devices {
|
||||||
|
key := dev.Name
|
||||||
|
if !seen[key] {
|
||||||
|
seen[key] = true
|
||||||
|
xcode.Simulators = append(xcode.Simulators, dev.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if xcode.Version == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return xcode
|
||||||
|
}
|
||||||
|
|
||||||
|
// detectLinuxDistro reads /etc/os-release to get distribution info
|
||||||
|
func detectLinuxDistro() *DistroInfo {
|
||||||
|
file, err := os.Open("/etc/os-release")
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
distro := &DistroInfo{}
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, "ID=") {
|
||||||
|
distro.ID = strings.Trim(strings.TrimPrefix(line, "ID="), "\"")
|
||||||
|
} else if strings.HasPrefix(line, "VERSION_ID=") {
|
||||||
|
distro.VersionID = strings.Trim(strings.TrimPrefix(line, "VERSION_ID="), "\"")
|
||||||
|
} else if strings.HasPrefix(line, "PRETTY_NAME=") {
|
||||||
|
distro.PrettyName = strings.Trim(strings.TrimPrefix(line, "PRETTY_NAME="), "\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if distro.ID == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return distro
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateSuggestedLabels creates industry-standard labels based on capabilities
|
||||||
|
func generateSuggestedLabels(cap *RunnerCapabilities) []string {
|
||||||
|
labels := []string{}
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
|
||||||
|
addLabel := func(label string) {
|
||||||
|
if label != "" && !seen[label] {
|
||||||
|
seen[label] = true
|
||||||
|
labels = append(labels, label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OS labels
|
||||||
|
switch cap.OS {
|
||||||
|
case "linux":
|
||||||
|
addLabel("linux")
|
||||||
|
addLabel("linux-latest")
|
||||||
|
case "windows":
|
||||||
|
addLabel("windows")
|
||||||
|
addLabel("windows-latest")
|
||||||
|
case "darwin":
|
||||||
|
addLabel("macos")
|
||||||
|
addLabel("macos-latest")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distro labels (Linux only)
|
||||||
|
if cap.Distro != nil && cap.Distro.ID != "" {
|
||||||
|
distro := strings.ToLower(cap.Distro.ID)
|
||||||
|
addLabel(distro)
|
||||||
|
addLabel(distro + "-latest")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Xcode/iOS labels (macOS only)
|
||||||
|
if cap.Xcode != nil {
|
||||||
|
addLabel("xcode")
|
||||||
|
// Check for SDKs
|
||||||
|
for _, sdk := range cap.Xcode.SDKs {
|
||||||
|
sdkLower := strings.ToLower(sdk)
|
||||||
|
if strings.Contains(sdkLower, "ios") {
|
||||||
|
addLabel("ios")
|
||||||
|
}
|
||||||
|
if strings.Contains(sdkLower, "visionos") || strings.Contains(sdkLower, "xros") {
|
||||||
|
addLabel("visionos")
|
||||||
|
}
|
||||||
|
if strings.Contains(sdkLower, "watchos") {
|
||||||
|
addLabel("watchos")
|
||||||
|
}
|
||||||
|
if strings.Contains(sdkLower, "tvos") {
|
||||||
|
addLabel("tvos")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If simulators available, add simulator label
|
||||||
|
if len(cap.Xcode.Simulators) > 0 {
|
||||||
|
addLabel("ios-simulator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tool-based labels
|
||||||
|
if _, ok := cap.Tools["dotnet"]; ok {
|
||||||
|
addLabel("dotnet")
|
||||||
|
}
|
||||||
|
if _, ok := cap.Tools["java"]; ok {
|
||||||
|
addLabel("java")
|
||||||
|
}
|
||||||
|
if _, ok := cap.Tools["node"]; ok {
|
||||||
|
addLabel("node")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build tool labels
|
||||||
|
for _, tool := range cap.BuildTools {
|
||||||
|
switch tool {
|
||||||
|
case "msbuild":
|
||||||
|
addLabel("msbuild")
|
||||||
|
case "visual-studio":
|
||||||
|
addLabel("vs2022") // or detect actual version
|
||||||
|
case "inno-setup":
|
||||||
|
addLabel("inno-setup")
|
||||||
|
case "nsis":
|
||||||
|
addLabel("nsis")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToJSON converts capabilities to JSON string for transmission
|
||||||
|
func (c *RunnerCapabilities) ToJSON() string {
|
||||||
|
data, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectShells() []string {
|
||||||
|
shells := []string{}
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
if _, err := exec.LookPath("pwsh"); err == nil {
|
||||||
|
shells = append(shells, "pwsh")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("powershell"); err == nil {
|
||||||
|
shells = append(shells, "powershell")
|
||||||
|
}
|
||||||
|
shells = append(shells, "cmd")
|
||||||
|
case "darwin":
|
||||||
|
if _, err := exec.LookPath("zsh"); err == nil {
|
||||||
|
shells = append(shells, "zsh")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("bash"); err == nil {
|
||||||
|
shells = append(shells, "bash")
|
||||||
|
}
|
||||||
|
shells = append(shells, "sh")
|
||||||
|
default: // linux and others
|
||||||
|
if _, err := exec.LookPath("bash"); err == nil {
|
||||||
|
shells = append(shells, "bash")
|
||||||
|
}
|
||||||
|
shells = append(shells, "sh")
|
||||||
|
}
|
||||||
|
|
||||||
|
return shells
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectDocker(ctx context.Context, dockerHost string) (bool, string) {
|
||||||
|
opts := []client.Opt{client.FromEnv}
|
||||||
|
if dockerHost != "" {
|
||||||
|
opts = append(opts, client.WithHost(dockerHost))
|
||||||
|
}
|
||||||
|
|
||||||
|
cli, err := client.NewClientWithOpts(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
defer cli.Close()
|
||||||
|
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err = cli.Ping(timeoutCtx)
|
||||||
|
if err != nil {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's podman or docker
|
||||||
|
info, err := cli.Info(timeoutCtx)
|
||||||
|
if err == nil {
|
||||||
|
if strings.Contains(strings.ToLower(info.Name), "podman") {
|
||||||
|
return true, "podman"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, "docker"
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectDockerCompose(ctx context.Context) bool {
|
||||||
|
// Check for docker compose v2 (docker compose)
|
||||||
|
cmd := exec.CommandContext(ctx, "docker", "compose", "version")
|
||||||
|
if err := cmd.Run(); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for docker-compose v1
|
||||||
|
if _, err := exec.LookPath("docker-compose"); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectTools(ctx context.Context, cap *RunnerCapabilities) {
|
||||||
|
toolDetectors := map[string]func(context.Context) []string{
|
||||||
|
"node": detectNodeVersions,
|
||||||
|
"go": detectGoVersions,
|
||||||
|
"python": detectPythonVersions,
|
||||||
|
"java": detectJavaVersions,
|
||||||
|
"dotnet": detectDotnetVersions,
|
||||||
|
"rust": detectRustVersions,
|
||||||
|
"ruby": detectRubyVersions,
|
||||||
|
"php": detectPHPVersions,
|
||||||
|
"swift": detectSwiftVersions,
|
||||||
|
"kotlin": detectKotlinVersions,
|
||||||
|
"flutter": detectFlutterVersions,
|
||||||
|
"dart": detectDartVersions,
|
||||||
|
"powershell": detectPowerShellVersions,
|
||||||
|
}
|
||||||
|
|
||||||
|
for tool, detector := range toolDetectors {
|
||||||
|
if versions := detector(ctx); len(versions) > 0 {
|
||||||
|
cap.Tools[tool] = versions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect additional tools that just need presence check
|
||||||
|
simpleTools := map[string]string{
|
||||||
|
"git": "git",
|
||||||
|
"cmake": "cmake",
|
||||||
|
"make": "make",
|
||||||
|
"ninja": "ninja",
|
||||||
|
"gradle": "gradle",
|
||||||
|
"maven": "mvn",
|
||||||
|
"npm": "npm",
|
||||||
|
"yarn": "yarn",
|
||||||
|
"pnpm": "pnpm",
|
||||||
|
"cargo": "cargo",
|
||||||
|
"pip": "pip3",
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, cmd := range simpleTools {
|
||||||
|
if v := detectSimpleToolVersion(ctx, cmd); v != "" {
|
||||||
|
cap.Tools[name] = []string{v}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectBuildTools(ctx context.Context, cap *RunnerCapabilities) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
detectWindowsBuildTools(ctx, cap)
|
||||||
|
case "darwin":
|
||||||
|
detectMacOSBuildTools(ctx, cap)
|
||||||
|
case "linux":
|
||||||
|
detectLinuxBuildTools(ctx, cap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) {
|
||||||
|
// Check for Visual Studio via vswhere
|
||||||
|
vswherePaths := []string{
|
||||||
|
`C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe`,
|
||||||
|
`C:\Program Files\Microsoft Visual Studio\Installer\vswhere.exe`,
|
||||||
|
}
|
||||||
|
for _, vswhere := range vswherePaths {
|
||||||
|
if _, err := os.Stat(vswhere); err == nil {
|
||||||
|
cmd := exec.CommandContext(ctx, vswhere, "-latest", "-property", "displayName")
|
||||||
|
if output, err := cmd.Output(); err == nil && len(output) > 0 {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "visual-studio")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for MSBuild
|
||||||
|
msbuildPaths := []string{
|
||||||
|
`C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe`,
|
||||||
|
`C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe`,
|
||||||
|
`C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe`,
|
||||||
|
`C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe`,
|
||||||
|
`C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\MSBuild.exe`,
|
||||||
|
`C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe`,
|
||||||
|
}
|
||||||
|
for _, msbuild := range msbuildPaths {
|
||||||
|
if _, err := os.Stat(msbuild); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "msbuild")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for Inno Setup
|
||||||
|
innoSetupPaths := []string{
|
||||||
|
`C:\Program Files (x86)\Inno Setup 6\ISCC.exe`,
|
||||||
|
`C:\Program Files\Inno Setup 6\ISCC.exe`,
|
||||||
|
`C:\Program Files (x86)\Inno Setup 5\ISCC.exe`,
|
||||||
|
`C:\Program Files\Inno Setup 5\ISCC.exe`,
|
||||||
|
}
|
||||||
|
for _, iscc := range innoSetupPaths {
|
||||||
|
if _, err := os.Stat(iscc); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "inno-setup")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Also check PATH
|
||||||
|
if _, err := exec.LookPath("iscc"); err == nil {
|
||||||
|
if !contains(cap.BuildTools, "inno-setup") {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "inno-setup")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for NSIS
|
||||||
|
nsisPaths := []string{
|
||||||
|
`C:\Program Files (x86)\NSIS\makensis.exe`,
|
||||||
|
`C:\Program Files\NSIS\makensis.exe`,
|
||||||
|
}
|
||||||
|
for _, nsis := range nsisPaths {
|
||||||
|
if _, err := os.Stat(nsis); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "nsis")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("makensis"); err == nil {
|
||||||
|
if !contains(cap.BuildTools, "nsis") {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "nsis")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for WiX Toolset
|
||||||
|
wixPaths := []string{
|
||||||
|
`C:\Program Files (x86)\WiX Toolset v3.11\bin\candle.exe`,
|
||||||
|
`C:\Program Files (x86)\WiX Toolset v3.14\bin\candle.exe`,
|
||||||
|
}
|
||||||
|
for _, wix := range wixPaths {
|
||||||
|
if _, err := os.Stat(wix); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "wix")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for signtool (Windows SDK)
|
||||||
|
signtoolPaths, _ := filepath.Glob(`C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe`)
|
||||||
|
if len(signtoolPaths) > 0 {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "signtool")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectMacOSBuildTools(ctx context.Context, cap *RunnerCapabilities) {
|
||||||
|
// Check for xcpretty
|
||||||
|
if _, err := exec.LookPath("xcpretty"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "xcpretty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for fastlane
|
||||||
|
if _, err := exec.LookPath("fastlane"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "fastlane")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for CocoaPods
|
||||||
|
if _, err := exec.LookPath("pod"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "cocoapods")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for Carthage
|
||||||
|
if _, err := exec.LookPath("carthage"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "carthage")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for SwiftLint
|
||||||
|
if _, err := exec.LookPath("swiftlint"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "swiftlint")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for create-dmg or similar
|
||||||
|
if _, err := exec.LookPath("create-dmg"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "create-dmg")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for Packages (packagesbuild)
|
||||||
|
if _, err := exec.LookPath("packagesbuild"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "packages")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for pkgbuild (built-in)
|
||||||
|
if _, err := exec.LookPath("pkgbuild"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "pkgbuild")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for codesign (built-in)
|
||||||
|
if _, err := exec.LookPath("codesign"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "codesign")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for notarytool (built-in with Xcode)
|
||||||
|
if _, err := exec.LookPath("notarytool"); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, "notarytool")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectLinuxBuildTools(ctx context.Context, cap *RunnerCapabilities) {
|
||||||
|
// Check for common Linux build tools
|
||||||
|
tools := []string{
|
||||||
|
"gcc", "g++", "clang", "clang++",
|
||||||
|
"autoconf", "automake", "libtool",
|
||||||
|
"pkg-config", "meson",
|
||||||
|
"dpkg-deb", "rpmbuild", "fpm",
|
||||||
|
"appimage-builder", "linuxdeploy",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tool := range tools {
|
||||||
|
if _, err := exec.LookPath(tool); err == nil {
|
||||||
|
cap.BuildTools = append(cap.BuildTools, tool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectPackageManagers(ctx context.Context, cap *RunnerCapabilities) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
if _, err := exec.LookPath("choco"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "chocolatey")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("scoop"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "scoop")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("winget"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "winget")
|
||||||
|
}
|
||||||
|
case "darwin":
|
||||||
|
if _, err := exec.LookPath("brew"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "homebrew")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("port"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "macports")
|
||||||
|
}
|
||||||
|
case "linux":
|
||||||
|
if _, err := exec.LookPath("apt"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "apt")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("yum"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "yum")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("dnf"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "dnf")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("pacman"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "pacman")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("zypper"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "zypper")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("apk"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "apk")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("snap"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "snap")
|
||||||
|
}
|
||||||
|
if _, err := exec.LookPath("flatpak"); err == nil {
|
||||||
|
cap.PackageManagers = append(cap.PackageManagers, "flatpak")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectNodeVersions(ctx context.Context) []string {
|
||||||
|
return detectToolVersion(ctx, "node", "--version", "v")
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectGoVersions(ctx context.Context) []string {
|
||||||
|
return detectToolVersion(ctx, "go", "version", "go")
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectPythonVersions(ctx context.Context) []string {
|
||||||
|
versions := []string{}
|
||||||
|
|
||||||
|
// Try python3 first
|
||||||
|
if v := detectToolVersion(ctx, "python3", "--version", "Python "); len(v) > 0 {
|
||||||
|
versions = append(versions, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also try python
|
||||||
|
if v := detectToolVersion(ctx, "python", "--version", "Python "); len(v) > 0 {
|
||||||
|
for _, ver := range v {
|
||||||
|
if !contains(versions, ver) {
|
||||||
|
versions = append(versions, ver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectJavaVersions(ctx context.Context) []string {
|
||||||
|
cmd := exec.CommandContext(ctx, "java", "-version")
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, "version") {
|
||||||
|
start := strings.Index(line, "\"")
|
||||||
|
end := strings.LastIndex(line, "\"")
|
||||||
|
if start != -1 && end > start {
|
||||||
|
version := line[start+1 : end]
|
||||||
|
parts := strings.Split(version, ".")
|
||||||
|
if len(parts) > 0 {
|
||||||
|
if parts[0] == "1" && len(parts) > 1 {
|
||||||
|
return []string{parts[1]}
|
||||||
|
}
|
||||||
|
return []string{parts[0]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectDotnetVersions(ctx context.Context) []string {
|
||||||
|
cmd := exec.CommandContext(ctx, "dotnet", "--list-sdks")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
versions := []string{}
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Split(line, " ")
|
||||||
|
if len(parts) > 0 {
|
||||||
|
version := parts[0]
|
||||||
|
major := strings.Split(version, ".")[0]
|
||||||
|
if !contains(versions, major) {
|
||||||
|
versions = append(versions, major)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectRustVersions(ctx context.Context) []string {
|
||||||
|
return detectToolVersion(ctx, "rustc", "--version", "rustc ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectRubyVersions(ctx context.Context) []string {
|
||||||
|
return detectToolVersion(ctx, "ruby", "--version", "ruby ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectPHPVersions(ctx context.Context) []string {
|
||||||
|
return detectToolVersion(ctx, "php", "--version", "PHP ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectSwiftVersions(ctx context.Context) []string {
|
||||||
|
return detectToolVersion(ctx, "swift", "--version", "Swift version ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectKotlinVersions(ctx context.Context) []string {
|
||||||
|
return detectToolVersion(ctx, "kotlin", "-version", "Kotlin version ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectFlutterVersions(ctx context.Context) []string {
|
||||||
|
return detectToolVersion(ctx, "flutter", "--version", "Flutter ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectDartVersions(ctx context.Context) []string {
|
||||||
|
return detectToolVersion(ctx, "dart", "--version", "Dart SDK version: ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectPowerShellVersions(ctx context.Context) []string {
|
||||||
|
versions := []string{}
|
||||||
|
|
||||||
|
// Check for pwsh (PowerShell Core / PowerShell 7+)
|
||||||
|
if v := detectPwshVersion(ctx, "pwsh"); v != "" {
|
||||||
|
versions = append(versions, "pwsh:"+v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for powershell (Windows PowerShell 5.x)
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if v := detectPwshVersion(ctx, "powershell"); v != "" {
|
||||||
|
versions = append(versions, "powershell:"+v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectPwshVersion(ctx context.Context, cmd string) string {
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Use -Command to get version
|
||||||
|
var c *exec.Cmd
|
||||||
|
if cmd == "pwsh" {
|
||||||
|
c = exec.CommandContext(timeoutCtx, cmd, "-Command", "$PSVersionTable.PSVersion.ToString()")
|
||||||
|
} else {
|
||||||
|
c = exec.CommandContext(timeoutCtx, cmd, "-Command", "$PSVersionTable.PSVersion.ToString()")
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := c.Output()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
version := strings.TrimSpace(string(output))
|
||||||
|
// Return major.minor
|
||||||
|
parts := strings.Split(version, ".")
|
||||||
|
if len(parts) >= 2 {
|
||||||
|
return parts[0] + "." + parts[1]
|
||||||
|
}
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectSimpleToolVersion(ctx context.Context, cmd string) string {
|
||||||
|
if _, err := exec.LookPath(cmd); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
c := exec.CommandContext(timeoutCtx, cmd, "--version")
|
||||||
|
output, err := c.Output()
|
||||||
|
if err != nil {
|
||||||
|
// Try without --version for tools that don't support it
|
||||||
|
return "installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
line := strings.TrimSpace(strings.Split(string(output), "\n")[0])
|
||||||
|
// Extract version number if possible
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
for _, part := range parts {
|
||||||
|
// Look for something that looks like a version
|
||||||
|
if len(part) > 0 && (part[0] >= '0' && part[0] <= '9' || part[0] == 'v') {
|
||||||
|
return strings.TrimPrefix(part, "v")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectToolVersion(ctx context.Context, cmd string, args string, prefix string) []string {
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
c := exec.CommandContext(timeoutCtx, cmd, args)
|
||||||
|
output, err := c.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
line := strings.TrimSpace(string(output))
|
||||||
|
if prefix != "" {
|
||||||
|
if idx := strings.Index(line, prefix); idx != -1 {
|
||||||
|
line = line[idx+len(prefix):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) > 0 {
|
||||||
|
version := parts[0]
|
||||||
|
version = strings.TrimPrefix(version, "v")
|
||||||
|
vparts := strings.Split(version, ".")
|
||||||
|
if len(vparts) >= 2 {
|
||||||
|
return []string{vparts[0] + "." + vparts[1]}
|
||||||
|
}
|
||||||
|
return []string{vparts[0]}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(slice []string, item string) bool {
|
||||||
|
for _, s := range slice {
|
||||||
|
if s == item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
43
internal/pkg/envcheck/disk_unix.go
Normal file
43
internal/pkg/envcheck/disk_unix.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//go:build unix
|
||||||
|
|
||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package envcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// detectDiskSpace detects disk space on the specified path's filesystem (Unix version)
|
||||||
|
// If path is empty, defaults to "/"
|
||||||
|
func detectDiskSpace(path string) *DiskInfo {
|
||||||
|
if path == "" {
|
||||||
|
path = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat unix.Statfs_t
|
||||||
|
|
||||||
|
err := unix.Statfs(path, &stat)
|
||||||
|
if err != nil {
|
||||||
|
// Fallback to root if the path doesn't exist
|
||||||
|
err = unix.Statfs("/", &stat)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
path = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
total := stat.Blocks * uint64(stat.Bsize)
|
||||||
|
free := stat.Bavail * uint64(stat.Bsize)
|
||||||
|
used := total - free
|
||||||
|
usedPercent := float64(used) / float64(total) * 100
|
||||||
|
|
||||||
|
return &DiskInfo{
|
||||||
|
Path: path,
|
||||||
|
Total: total,
|
||||||
|
Free: free,
|
||||||
|
Used: used,
|
||||||
|
UsedPercent: usedPercent,
|
||||||
|
}
|
||||||
|
}
|
||||||
57
internal/pkg/envcheck/disk_windows.go
Normal file
57
internal/pkg/envcheck/disk_windows.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package envcheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// detectDiskSpace detects disk space on the specified path's drive (Windows version)
|
||||||
|
// If path is empty, defaults to "C:\"
|
||||||
|
func detectDiskSpace(path string) *DiskInfo {
|
||||||
|
if path == "" {
|
||||||
|
path = "C:\\"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve to absolute path
|
||||||
|
absPath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
absPath = "C:\\"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract drive letter (e.g., "D:\" from "D:\builds\runner")
|
||||||
|
drivePath := filepath.VolumeName(absPath) + "\\"
|
||||||
|
if drivePath == "\\" {
|
||||||
|
drivePath = "C:\\"
|
||||||
|
}
|
||||||
|
|
||||||
|
var freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes uint64
|
||||||
|
|
||||||
|
pathPtr := windows.StringToUTF16Ptr(drivePath)
|
||||||
|
err = windows.GetDiskFreeSpaceEx(pathPtr, &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes)
|
||||||
|
if err != nil {
|
||||||
|
// Fallback to C: drive
|
||||||
|
pathPtr = windows.StringToUTF16Ptr("C:\\")
|
||||||
|
err = windows.GetDiskFreeSpaceEx(pathPtr, &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
drivePath = "C:\\"
|
||||||
|
}
|
||||||
|
|
||||||
|
used := totalNumberOfBytes - totalNumberOfFreeBytes
|
||||||
|
usedPercent := float64(used) / float64(totalNumberOfBytes) * 100
|
||||||
|
|
||||||
|
return &DiskInfo{
|
||||||
|
Path: drivePath,
|
||||||
|
Total: totalNumberOfBytes,
|
||||||
|
Free: totalNumberOfFreeBytes,
|
||||||
|
Used: used,
|
||||||
|
UsedPercent: usedPercent,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,7 +55,6 @@ func (l Labels) PickPlatform(runsOn []string) string {
|
|||||||
switch label.Schema {
|
switch label.Schema {
|
||||||
case SchemeDocker:
|
case SchemeDocker:
|
||||||
// "//" will be ignored
|
// "//" will be ignored
|
||||||
// TODO maybe we should use 'ubuntu-18.04:docker:node:16-buster' instead
|
|
||||||
platforms[label.Name] = strings.TrimPrefix(label.Arg, "//")
|
platforms[label.Name] = strings.TrimPrefix(label.Arg, "//")
|
||||||
case SchemeHost:
|
case SchemeHost:
|
||||||
platforms[label.Name] = "-self-hosted"
|
platforms[label.Name] = "-self-hosted"
|
||||||
@@ -80,7 +79,7 @@ func (l Labels) PickPlatform(runsOn []string) string {
|
|||||||
// So the runner receives a task with a label that the runner doesn't have,
|
// So the runner receives a task with a label that the runner doesn't have,
|
||||||
// it happens when the user have edited the label of the runner in the web UI.
|
// it happens when the user have edited the label of the runner in the web UI.
|
||||||
// TODO: it may be not correct, what if the runner is used as host mode only?
|
// TODO: it may be not correct, what if the runner is used as host mode only?
|
||||||
return "node:16-bullseye"
|
return "docker.gitea.com/runner-images:ubuntu-latest"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Labels) Names() []string {
|
func (l Labels) Names() []string {
|
||||||
|
|||||||
@@ -143,6 +143,12 @@ func (r *Reporter) Fire(entry *log.Entry) error {
|
|||||||
if step.StartedAt == nil {
|
if step.StartedAt == nil {
|
||||||
step.StartedAt = timestamppb.New(timestamp)
|
step.StartedAt = timestamppb.New(timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force reporting log errors as raw output to prevent silent failures
|
||||||
|
if entry.Level == log.ErrorLevel {
|
||||||
|
entry.Data["raw_output"] = true
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := entry.Data["raw_output"]; ok {
|
if v, ok := entry.Data["raw_output"]; ok {
|
||||||
if rawOutput, ok := v.(bool); ok && rawOutput {
|
if rawOutput, ok := v.(bool); ok && rawOutput {
|
||||||
if row := r.parseLogRow(entry); row != nil {
|
if row := r.parseLogRow(entry); row != nil {
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# wait for docker daemon
|
|
||||||
while ! nc -z localhost 2376 </dev/null; do
|
|
||||||
echo 'waiting for docker daemon...'
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
|
|
||||||
. /opt/act/run.sh
|
|
||||||
@@ -16,6 +16,13 @@ EXTRA_ARGS=""
|
|||||||
if [[ ! -z "${GITEA_RUNNER_LABELS}" ]]; then
|
if [[ ! -z "${GITEA_RUNNER_LABELS}" ]]; then
|
||||||
EXTRA_ARGS="${EXTRA_ARGS} --labels ${GITEA_RUNNER_LABELS}"
|
EXTRA_ARGS="${EXTRA_ARGS} --labels ${GITEA_RUNNER_LABELS}"
|
||||||
fi
|
fi
|
||||||
|
if [[ ! -z "${GITEA_RUNNER_EPHEMERAL}" ]]; then
|
||||||
|
EXTRA_ARGS="${EXTRA_ARGS} --ephemeral"
|
||||||
|
fi
|
||||||
|
RUN_ARGS=""
|
||||||
|
if [[ ! -z "${GITEA_RUNNER_ONCE}" ]]; then
|
||||||
|
RUN_ARGS="${RUN_ARGS} --once"
|
||||||
|
fi
|
||||||
|
|
||||||
# In case no token is set, it's possible to read the token from a file, i.e. a Docker Secret
|
# In case no token is set, it's possible to read the token from a file, i.e. a Docker Secret
|
||||||
if [[ -z "${GITEA_RUNNER_REGISTRATION_TOKEN}" ]] && [[ -f "${GITEA_RUNNER_REGISTRATION_TOKEN_FILE}" ]]; then
|
if [[ -z "${GITEA_RUNNER_REGISTRATION_TOKEN}" ]] && [[ -f "${GITEA_RUNNER_REGISTRATION_TOKEN_FILE}" ]]; then
|
||||||
@@ -54,4 +61,4 @@ fi
|
|||||||
unset GITEA_RUNNER_REGISTRATION_TOKEN
|
unset GITEA_RUNNER_REGISTRATION_TOKEN
|
||||||
unset GITEA_RUNNER_REGISTRATION_TOKEN_FILE
|
unset GITEA_RUNNER_REGISTRATION_TOKEN_FILE
|
||||||
|
|
||||||
act_runner daemon ${CONFIG_ARG}
|
exec act_runner daemon ${CONFIG_ARG} ${RUN_ARGS}
|
||||||
|
|||||||
3
scripts/s6/act_runner/finish
Executable file
3
scripts/s6/act_runner/finish
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
exec s6-svscanctl -t /etc/s6
|
||||||
5
scripts/s6/act_runner/run
Executable file
5
scripts/s6/act_runner/run
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
s6-svwait -U /etc/s6/docker
|
||||||
|
|
||||||
|
exec run.sh
|
||||||
6
scripts/s6/docker/data/check
Executable file
6
scripts/s6/docker/data/check
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if ! docker info &> /dev/null; then
|
||||||
|
echo "Waiting for Docker daemon to start..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
4
scripts/s6/docker/finish
Executable file
4
scripts/s6/docker/finish
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
exec s6-svscanctl -t /etc/s6
|
||||||
|
|
||||||
1
scripts/s6/docker/notification-fd
Normal file
1
scripts/s6/docker/notification-fd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3
|
||||||
3
scripts/s6/docker/run
Executable file
3
scripts/s6/docker/run
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
exec s6-notifyoncheck dockerd-entrypoint.sh
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
[supervisord]
|
|
||||||
nodaemon=true
|
|
||||||
logfile=/dev/null
|
|
||||||
logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:dockerd]
|
|
||||||
command=/usr/local/bin/dockerd-entrypoint.sh
|
|
||||||
|
|
||||||
[program:act_runner]
|
|
||||||
stdout_logfile=/dev/fd/1
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
redirect_stderr=true
|
|
||||||
command=/opt/act/rootless.sh
|
|
||||||
|
|
||||||
[eventlistener:processes]
|
|
||||||
command=bash -c "echo READY && read line && kill -SIGQUIT $PPID"
|
|
||||||
events=PROCESS_STATE_STOPPED,PROCESS_STATE_EXITED,PROCESS_STATE_FATAL
|
|
||||||
Reference in New Issue
Block a user