5 Commits

Author SHA1 Message Date
Renovate Bot
589db33e70 fix(deps): update module github.com/docker/cli to v25.0.7+incompatible (#855)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/docker/cli](https://github.com/docker/cli) | `v25.0.3+incompatible` → `v25.0.7+incompatible` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fdocker%2fcli/v25.0.7+incompatible?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fdocker%2fcli/v25.0.3+incompatible/v25.0.7+incompatible?slim=true) |

---

### Release Notes

<details>
<summary>docker/cli (github.com/docker/cli)</summary>

### [`v25.0.7+incompatible`](https://github.com/docker/cli/compare/v25.0.6...v25.0.7)

[Compare Source](https://github.com/docker/cli/compare/v25.0.6...v25.0.7)

### [`v25.0.6+incompatible`](https://github.com/docker/cli/compare/v25.0.5...v25.0.6)

[Compare Source](https://github.com/docker/cli/compare/v25.0.5...v25.0.6)

### [`v25.0.5+incompatible`](https://github.com/docker/cli/compare/v25.0.4...v25.0.5)

[Compare Source](https://github.com/docker/cli/compare/v25.0.4...v25.0.5)

### [`v25.0.4+incompatible`](https://github.com/docker/cli/compare/v25.0.3...v25.0.4)

[Compare Source](https://github.com/docker/cli/compare/v25.0.3...v25.0.4)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuNSIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/855
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-04-27 01:01:35 +00:00
Renovate Bot
1032f857a1 fix(deps): update module connectrpc.com/connect to v1.19.2 (#854)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [connectrpc.com/connect](https://github.com/connectrpc/connect-go) | `v1.19.1` → `v1.19.2` | ![age](https://developer.mend.io/api/mc/badges/age/go/connectrpc.com%2fconnect/v1.19.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/connectrpc.com%2fconnect/v1.19.1/v1.19.2?slim=true) |

---

### Release Notes

<details>
<summary>connectrpc/connect-go (connectrpc.com/connect)</summary>

### [`v1.19.2`](https://github.com/connectrpc/connect-go/releases/tag/v1.19.2)

[Compare Source](https://github.com/connectrpc/connect-go/compare/v1.19.1...v1.19.2)

#### What's Changed

##### Governance

- Add [@&#8203;timostamm](https://github.com/timostamm) as a maintainer in [#&#8203;905](https://github.com/connectrpc/connect-go/pull/905) 🎉

##### Bugfixes

- Use 'deadline\_exceeded' instead of 'canceled' on HTTP/2 cancelation when appropriate by [@&#8203;jhump](https://github.com/jhump) in [#&#8203;904](https://github.com/connectrpc/connect-go/pull/904)
- Fix nil pointer deref in duplexHTTPCall under concurrent Send + CloseAndReceive by [@&#8203;simonferquel](https://github.com/simonferquel) in [#&#8203;919](https://github.com/connectrpc/connect-go/pull/919)

##### Other changes

- Refactor memhttptest to work with Go 1.25 synctest by [@&#8203;codefromthecrypt](https://github.com/codefromthecrypt) in [#&#8203;881](https://github.com/connectrpc/connect-go/pull/881)
- Doc clarifications by [@&#8203;emcfarlane](https://github.com/emcfarlane) ([#&#8203;911](https://github.com/connectrpc/connect-go/issues/911), [#&#8203;912](https://github.com/connectrpc/connect-go/issues/912)) and [@&#8203;stefanvanburen](https://github.com/stefanvanburen) ([#&#8203;906](https://github.com/connectrpc/connect-go/issues/906))

#### New Contributors

- [@&#8203;codefromthecrypt](https://github.com/codefromthecrypt) made their first contribution in [#&#8203;881](https://github.com/connectrpc/connect-go/pull/881)
- [@&#8203;simonferquel](https://github.com/simonferquel) made their first contribution in [#&#8203;919](https://github.com/connectrpc/connect-go/pull/919)

**Full Changelog**:

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuNSIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/854
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-04-27 01:01:16 +00:00
silverwind
e56b984c04 fix: wait for docker supervise dir before s6-svwait (#851)
`s6-svscan` starts services in parallel, so `act_runner/run` could invoke `s6-svwait` before s6 had created the docker service's `supervise/` directory, failing with `s6-svwait: fatal: unable to s6_svstatus_read: No such file or directory`. Poll for the directory before waiting.

Fixes #760

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/851
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-04-26 22:50:48 +00:00
silverwind
fa5334eb24 fix: Heartbeat ReportState for long-running silent jobs (#852)
Fixes #826.

Regressed in f2d54556 (#819, "perf: reduce runner-to-server connection load with adaptive reporting and polling"). That change added an early-return in `ReportState` whenever there was no state change and no pending outputs, so jobs that produce no log output and no step transitions for many minutes (e.g. a Linux kernel build) stop heartbeating. The server eventually marks the task as orphaned and cancels it while the runner is still executing.

The fix tracks the last successful `UpdateTask` time in an atomic and keeps the no-op skip only while the previous report is younger than `stateReportInterval`. The periodic state ticker fires at exactly `stateReportInterval`, so silent jobs now heartbeat each tick; redundant sends from a `stateNotify` firing right after a tick are still suppressed, preserving the perf intent of #819.

Test added: `TestReporter_StateHeartbeat` asserts the skip path within the interval and the heartbeat path after the interval elapses.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/852
Reviewed-by: Nicolas <bircni@icloud.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: ChristopherHX <38043+christopherhx@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-04-26 22:49:39 +00:00
Michael Hoang
7c6f1261d4 fix: fetch when other refs get force-pushed (#846)
Due to `NewGitCloneExecutor` fetching all the refs (rather than `GoGitActionCache`), if any refs move in a non-fast-forward fashion, this causes the entire action update to fail. As GitHub has special refs like `pull/N/merge` which are guaranteed to move in a non-fast-forward fashion, this leads actions from GitHub usually failing to update.

```
  ☁  git clone 'https://github.com/Mic92/update-flake-inputs-gitea' # ref=main
  cloning https://github.com/Mic92/update-flake-inputs-gitea to /var/lib/gitea-runner/nix0/.cache/act/9b0155f2957ac84c749f9ecc8afaec823af5ef2e67a104ac655623aee12ca5b2
Non-terminating error while running 'git clone': some refs were not updated
```

With the repo https://github.com/Mic92/update-flake-inputs-gitea, you can notice that it only has a `main` branch that moves in a fast-forward fashion and no tags that could've been force pushed.

Fixes #726

---------

Co-authored-by: Michael Hoang <enzime@users.noreply.github.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/846
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Michael Hoang <194829+enzime@noreply.gitea.com>
Co-committed-by: Michael Hoang <194829+enzime@noreply.gitea.com>
2026-04-26 11:08:23 +00:00
93 changed files with 375 additions and 254 deletions

View File

@@ -40,7 +40,7 @@ cpu.out
*.db *.db
*.log *.log
/runner /act_runner
/debug /debug
/bin /bin

View File

@@ -69,7 +69,7 @@ jobs:
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Echo the tag - name: Echo the tag
run: echo "${{ env.DOCKER_ORG }}/runner:nightly${{ matrix.variant.tag_suffix }}" run: echo "${{ env.DOCKER_ORG }}/act_runner:nightly${{ matrix.variant.tag_suffix }}"
- name: Build and push - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
@@ -82,4 +82,4 @@ jobs:
linux/arm64 linux/arm64
push: true push: true
tags: | tags: |
${{ env.DOCKER_ORG }}/runner:nightly${{ matrix.variant.tag_suffix }} ${{ env.DOCKER_ORG }}/act_runner:nightly${{ matrix.variant.tag_suffix }}

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
/runner /act_runner
.env .env
.runner .runner
coverage.txt coverage.txt

View File

@@ -114,7 +114,7 @@ formatters:
custom-order: true custom-order: true
sections: sections:
- standard - standard
- prefix(gitea.com/gitea/runner) - prefix(gitea.com/gitea/act_runner)
- blank - blank
- default - default
gofumpt: gofumpt:

View File

@@ -63,7 +63,7 @@ builds:
flags: flags:
- -trimpath - -trimpath
ldflags: ldflags:
- -s -w -X gitea.com/gitea/runner/internal/pkg/ver.version={{ .Summary }} - -s -w -X gitea.com/gitea/act_runner/internal/pkg/ver.version={{ .Summary }}
binary: >- binary: >-
{{ .ProjectName }}- {{ .ProjectName }}-
{{- .Version }}- {{- .Version }}-
@@ -86,7 +86,7 @@ blobs:
provider: s3 provider: s3
bucket: "{{ .Env.S3_BUCKET }}" bucket: "{{ .Env.S3_BUCKET }}"
region: "{{ .Env.S3_REGION }}" region: "{{ .Env.S3_REGION }}"
directory: "runner/{{.Version}}" directory: "act_runner/{{.Version}}"
extra_files: extra_files:
- glob: ./**.xz - glob: ./**.xz
- glob: ./**.sha256 - glob: ./**.sha256

View File

@@ -9,8 +9,8 @@ RUN apk add --no-cache make git
ARG GOPROXY ARG GOPROXY
ENV GOPROXY=${GOPROXY:-} ENV GOPROXY=${GOPROXY:-}
COPY . /opt/src/runner COPY . /opt/src/act_runner
WORKDIR /opt/src/runner WORKDIR /opt/src/act_runner
RUN make clean && make build RUN make clean && make build
@@ -21,7 +21,7 @@ FROM docker:28-dind AS dind
RUN apk add --no-cache s6 bash git tzdata RUN apk add --no-cache s6 bash git tzdata
COPY --from=builder /opt/src/runner/runner /usr/local/bin/runner 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/run.sh /usr/local/bin/run.sh
COPY scripts/s6 /etc/s6 COPY scripts/s6 /etc/s6
@@ -37,7 +37,7 @@ FROM docker:28-dind-rootless AS dind-rootless
USER root USER root
RUN apk add --no-cache s6 bash git tzdata RUN apk add --no-cache s6 bash git tzdata
COPY --from=builder /opt/src/runner/runner /usr/local/bin/runner 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/run.sh /usr/local/bin/run.sh
COPY scripts/s6 /etc/s6 COPY scripts/s6 /etc/s6
@@ -56,7 +56,7 @@ ENTRYPOINT ["s6-svscan","/etc/s6"]
FROM alpine AS basic FROM alpine AS basic
RUN apk add --no-cache tini bash git tzdata RUN apk add --no-cache tini bash git tzdata
COPY --from=builder /opt/src/runner/runner /usr/local/bin/runner 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/run.sh /usr/local/bin/run.sh
VOLUME /data VOLUME /data

View File

@@ -1,5 +1,5 @@
DIST := dist DIST := dist
EXECUTABLE := runner EXECUTABLE := act_runner
DIST_DIRS := $(DIST)/binaries $(DIST)/release DIST_DIRS := $(DIST)/binaries $(DIST)/release
GO ?= go GO ?= go
SHASUM ?= shasum -a 256 SHASUM ?= shasum -a 256
@@ -13,7 +13,7 @@ DARWIN_ARCHS ?= darwin-12/amd64,darwin-12/arm64
WINDOWS_ARCHS ?= windows/amd64 WINDOWS_ARCHS ?= windows/amd64
GOFILES := $(shell find . -type f -name "*.go" -o -name "go.mod" ! -name "generated.*") GOFILES := $(shell find . -type f -name "*.go" -o -name "go.mod" ! -name "generated.*")
DOCKER_IMAGE ?= gitea/runner DOCKER_IMAGE ?= gitea/act_runner
DOCKER_TAG ?= nightly 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
@@ -67,7 +67,7 @@ else
endif endif
TAGS ?= TAGS ?=
LDFLAGS ?= -X "gitea.com/gitea/runner/internal/pkg/ver.version=v$(RELASE_VERSION)" LDFLAGS ?= -X "gitea.com/gitea/act_runner/internal/pkg/ver.version=v$(RELASE_VERSION)"
.PHONY: all .PHONY: all
all: build all: build
@@ -86,7 +86,7 @@ go-check:
$(eval MIN_GO_VERSION := $(shell printf "%03d%03d" $(shell echo '$(MIN_GO_VERSION_STR)' | tr '.' ' '))) $(eval MIN_GO_VERSION := $(shell printf "%03d%03d" $(shell echo '$(MIN_GO_VERSION_STR)' | tr '.' ' ')))
$(eval GO_VERSION := $(shell printf "%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9]+' | tr '.' ' ');)) $(eval GO_VERSION := $(shell printf "%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9]+' | tr '.' ' ');))
@if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \ @if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \
echo "Gitea Runner requires Go $(MIN_GO_VERSION_STR) or greater to build. You can get it at https://go.dev/dl/"; \ echo "Act Runner requires Go $(MIN_GO_VERSION_STR) or greater to build. You can get it at https://go.dev/dl/"; \
exit 1; \ exit 1; \
fi fi
@@ -140,11 +140,11 @@ test: fmt-check security-check ## test everything
@$(GO) test -race -short -v -cover -coverprofile coverage.txt ./... && echo "\n==>\033[32m Ok\033[m\n" || exit 1 @$(GO) test -race -short -v -cover -coverprofile coverage.txt ./... && echo "\n==>\033[32m Ok\033[m\n" || exit 1
.PHONY: install .PHONY: install
install: $(GOFILES) ## install the runner binary via `go install` install: $(GOFILES) ## install the act_runner binary via `go install`
$(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)'
.PHONY: build .PHONY: build
build: go-check $(EXECUTABLE) ## build the runner binary build: go-check $(EXECUTABLE) ## build the act_runner binary
$(EXECUTABLE): $(GOFILES) $(EXECUTABLE): $(GOFILES)
$(GO) build -v -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@ $(GO) build -v -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@

View File

@@ -1,4 +1,6 @@
# Gitea Runner # act runner
Act runner is a runner for Gitea.
## Installation ## Installation
@@ -8,7 +10,7 @@ Docker Engine Community version is required for docker mode. To install Docker C
### Download pre-built binary ### Download pre-built binary
Visit [here](https://dl.gitea.com/runner/) and download the right version for your platform. Visit [here](https://dl.gitea.com/act_runner/) and download the right version for your platform.
### Build from source ### Build from source
@@ -34,7 +36,7 @@ ENABLED=true
### Register ### Register
```bash ```bash
./runner register ./act_runner register
``` ```
And you will be asked to input: And you will be asked to input:
@@ -66,7 +68,7 @@ INFO Runner registered successfully.
You can also register with command line arguments. You can also register with command line arguments.
```bash ```bash
./runner register --instance http://192.168.8.8:3000 --token <my_runner_token> --no-interactive ./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. If the registry succeed, it will run immediately. Next time, you could run the runner directly.
@@ -74,29 +76,29 @@ If the registry succeed, it will run immediately. Next time, you could run the r
### Run ### Run
```bash ```bash
./runner daemon ./act_runner daemon
``` ```
### Run with docker ### Run with docker
```bash ```bash
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/runner:nightly 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
``` ```
### Configuration ### Configuration
You can also configure the runner with a configuration file. You can also configure the runner with a configuration file.
The configuration file is a YAML file, you can generate a sample configuration file with `./runner generate-config`. The configuration file is a YAML file, you can generate a sample configuration file with `./act_runner generate-config`.
```bash ```bash
./runner generate-config > config.yaml ./act_runner generate-config > config.yaml
``` ```
You can specify the configuration file path with `-c`/`--config` argument. You can specify the configuration file path with `-c`/`--config` argument.
```bash ```bash
./runner -c config.yaml register # register with config file ./act_runner -c config.yaml register # register with config file
./runner -c config.yaml daemon # run with config file ./act_runner -c config.yaml daemon # run with config file
``` ```
You can read the latest version of the configuration file online at [config.example.yaml](internal/pkg/config/config.example.yaml). You can read the latest version of the configuration file online at [config.example.yaml](internal/pkg/config/config.example.yaml).

View File

@@ -19,7 +19,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"

View File

@@ -17,7 +17,7 @@ import (
"strings" "strings"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
) )

View File

@@ -17,8 +17,8 @@ import (
"testing" "testing"
"testing/fstest" "testing/fstest"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"gitea.com/gitea/runner/act/runner" "gitea.com/gitea/act_runner/act/runner"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"

View File

@@ -117,7 +117,7 @@ func NewParallelExecutor(parallel int, executors ...Executor) Executor {
log.Debugf("Worker %d executing task %d", workerID, taskCount) log.Debugf("Worker %d executing task %d", workerID, taskCount)
// Recover from panics in executors to avoid crashing the worker // Recover from panics in executors to avoid crashing the worker
// goroutine which would leave the runner process hung. // goroutine which would leave the runner process hung.
// https://gitea.com/gitea/runner/issues/371 // https://gitea.com/gitea/act_runner/issues/371
errs <- func() (err error) { errs <- func() (err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {

View File

@@ -15,7 +15,7 @@ import (
"strings" "strings"
"sync" "sync"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
@@ -277,6 +277,7 @@ func CloneIfRequired(ctx context.Context, refName plumbing.ReferenceName, input
func gitOptions(token string) (fetchOptions git.FetchOptions, pullOptions git.PullOptions) { func gitOptions(token string) (fetchOptions git.FetchOptions, pullOptions git.PullOptions) {
fetchOptions.RefSpecs = []config.RefSpec{"refs/*:refs/*", "HEAD:refs/heads/HEAD"} fetchOptions.RefSpecs = []config.RefSpec{"refs/*:refs/*", "HEAD:refs/heads/HEAD"}
fetchOptions.Force = true
pullOptions.Force = true pullOptions.Force = true
if token != "" { if token != "" {

View File

@@ -10,6 +10,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
"syscall" "syscall"
"testing" "testing"
@@ -220,6 +221,62 @@ func TestGitCloneExecutor(t *testing.T) {
} }
} }
func TestGitCloneExecutorNonFastForwardRef(t *testing.T) {
// Simulate the scenario where a remote ref (e.g. a GitHub PR head ref) changes
// non-fast-forward between two fetches. Before the fix, the fetch used Force=false,
// causing go-git to return ErrForceNeeded and short-circuit the checkout.
gitConfig()
// Create a bare "remote" repo with an initial commit on main and a feature branch.
remoteDir := t.TempDir()
require.NoError(t, gitCmd("init", "--bare", "--initial-branch=main", remoteDir))
// We need a working clone to push commits from.
workDir := t.TempDir()
require.NoError(t, gitCmd("clone", remoteDir, workDir))
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "main"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "initial"))
require.NoError(t, gitCmd("-C", workDir, "push", "-u", "origin", "main"))
// Create a feature branch (simulates refs/pull/N/head).
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "feature"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "feature-1"))
require.NoError(t, gitCmd("-C", workDir, "push", "origin", "feature"))
// First clone via the executor — should succeed and cache the repo.
cloneDir := t.TempDir()
clone := NewGitCloneExecutor(NewGitCloneExecutorInput{
URL: remoteDir,
Ref: "main",
Dir: cloneDir,
})
require.NoError(t, clone(context.Background()))
// Now force-push the feature branch to a non-fast-forward commit (simulates
// a PR rebase). This makes refs/heads/feature non-fast-forward.
require.NoError(t, gitCmd("-C", workDir, "checkout", "main"))
require.NoError(t, gitCmd("-C", workDir, "branch", "-D", "feature"))
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "feature"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "feature-rewritten"))
require.NoError(t, gitCmd("-C", workDir, "push", "--force", "origin", "feature"))
// Also advance main so we can verify the clone picks up the new commit.
require.NoError(t, gitCmd("-C", workDir, "checkout", "main"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "second"))
require.NoError(t, gitCmd("-C", workDir, "push", "origin", "main"))
// Second clone to the same directory — before the fix this returned ErrForceNeeded
// and left the working tree at the old commit.
err := clone(context.Background())
require.NoError(t, err, "fetch with non-fast-forward refs must not fail when Force=true")
// Verify the working tree was actually updated to the latest main commit.
out, err := exec.Command("git", "-C", cloneDir, "log", "--oneline", "-1", "--format=%s").Output()
require.NoError(t, err)
assert.Equal(t, "second", strings.TrimSpace(string(out)), "working tree should be at the latest commit")
}
func gitConfig() { func gitConfig() {
if os.Getenv("GITHUB_ACTIONS") == "true" { if os.Getenv("GITHUB_ACTIONS") == "true" {
var err error var err error

View File

@@ -8,7 +8,7 @@ import (
"context" "context"
"io" "io"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
) )

View File

@@ -10,7 +10,7 @@ import (
"context" "context"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/credentials" "github.com/docker/cli/cli/config/credentials"

View File

@@ -12,10 +12,11 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
// github.com/docker/docker/builder/dockerignore is deprecated
"github.com/moby/buildkit/frontend/dockerfile/dockerignore" "github.com/moby/buildkit/frontend/dockerfile/dockerignore"
"github.com/moby/patternmatcher" "github.com/moby/patternmatcher"
) )

View File

@@ -9,7 +9,7 @@ package container
import ( import (
"context" "context"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
) )

View File

@@ -13,7 +13,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/distribution/reference" "github.com/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"

View File

@@ -20,8 +20,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/filecollector" "gitea.com/gitea/act_runner/act/filecollector"
"github.com/Masterminds/semver" "github.com/Masterminds/semver"
"github.com/docker/cli/cli/compose/loader" "github.com/docker/cli/cli/compose/loader"

View File

@@ -15,7 +15,7 @@ import (
"testing" "testing"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"

View File

@@ -10,7 +10,7 @@ import (
"context" "context"
"runtime" "runtime"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/pkg/errors" "github.com/pkg/errors"

View File

@@ -9,7 +9,7 @@ package container
import ( import (
"context" "context"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/volume" "github.com/docker/docker/api/types/volume"

View File

@@ -19,9 +19,9 @@ import (
"strings" "strings"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/filecollector" "gitea.com/gitea/act_runner/act/filecollector"
"gitea.com/gitea/runner/act/lookpath" "gitea.com/gitea/act_runner/act/lookpath"
"github.com/go-git/go-billy/v5/helper/polyfill" "github.com/go-git/go-billy/v5/helper/polyfill"
"github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-billy/v5/osfs"

View File

@@ -12,7 +12,7 @@ import (
"io" "io"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
) )
func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Executor { func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Executor {

View File

@@ -18,7 +18,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/go-git/go-git/v5/plumbing/format/gitignore" "github.com/go-git/go-git/v5/plumbing/format/gitignore"
"github.com/rhysd/actionlint" "github.com/rhysd/actionlint"

View File

@@ -8,7 +8,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )

View File

@@ -12,7 +12,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/rhysd/actionlint" "github.com/rhysd/actionlint"
) )

View File

@@ -8,7 +8,7 @@ import (
"math" "math"
"testing" "testing"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )

View File

@@ -9,8 +9,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/common/git" "gitea.com/gitea/act_runner/act/common/git"
) )
type GithubContext struct { type GithubContext struct {

View File

@@ -15,7 +15,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"go.yaml.in/yaml/v4" "go.yaml.in/yaml/v4"

View File

@@ -18,9 +18,9 @@ import (
"runtime" "runtime"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/kballard/go-shellquote" "github.com/kballard/go-shellquote"
) )

View File

@@ -11,8 +11,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
) )
func evaluateCompositeInputAndEnv(ctx context.Context, parent *RunContext, step actionStep) map[string]string { func evaluateCompositeInputAndEnv(ctx context.Context, parent *RunContext, step actionStep) map[string]string {

View File

@@ -11,7 +11,7 @@ import (
"strings" "strings"
"testing" "testing"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"

View File

@@ -9,7 +9,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
) )
var commandPatternGA *regexp.Regexp var commandPatternGA *regexp.Regexp

View File

@@ -11,8 +11,8 @@ import (
"os" "os"
"testing" "testing"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/sirupsen/logrus/hooks/test" "github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"

View File

@@ -8,8 +8,8 @@ import (
"context" "context"
"io" "io"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
) )

View File

@@ -15,10 +15,10 @@ import (
"strings" "strings"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"gitea.com/gitea/runner/act/exprparser" "gitea.com/gitea/act_runner/act/exprparser"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
_ "embed" _ "embed"

View File

@@ -8,8 +8,8 @@ import (
"context" "context"
"testing" "testing"
"gitea.com/gitea/runner/act/exprparser" "gitea.com/gitea/act_runner/act/exprparser"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
assert "github.com/stretchr/testify/assert" assert "github.com/stretchr/testify/assert"
yaml "go.yaml.in/yaml/v4" yaml "go.yaml.in/yaml/v4"

View File

@@ -10,8 +10,8 @@ import (
"strconv" "strconv"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
) )
type jobInfo interface { type jobInfo interface {
@@ -137,7 +137,7 @@ func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executo
// if !rc.IsHostEnv(ctx) && rc.Config.ContainerNetworkMode == "" { // if !rc.IsHostEnv(ctx) && rc.Config.ContainerNetworkMode == "" {
// // clean network in docker mode only // // clean network in docker mode only
// // if the value of `ContainerNetworkMode` is empty string, // // if the value of `ContainerNetworkMode` is empty string,
// // it means that the network to which containers are connecting is created by `runner`, // // it means that the network to which containers are connecting is created by `act_runner`,
// // so, we should remove the network at last. // // so, we should remove the network at last.
// networkName, _ := rc.networkName() // networkName, _ := rc.networkName()
// logger.Infof("Cleaning up network for job %s, and network name is: %s", rc.JobName, networkName) // logger.Infof("Cleaning up network for job %s, and network name is: %s", rc.JobName, networkName)

View File

@@ -12,9 +12,9 @@ import (
"slices" "slices"
"testing" "testing"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"

View File

@@ -16,7 +16,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"gitea.com/gitea/runner/act/filecollector" "gitea.com/gitea/act_runner/act/filecollector"
) )
type LocalRepositoryCache struct { type LocalRepositoryCache struct {

View File

@@ -13,7 +13,7 @@ import (
"strings" "strings"
"sync" "sync"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/term" "golang.org/x/term"

View File

@@ -6,7 +6,7 @@ package runner
import ( import (
"testing" "testing"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go.yaml.in/yaml/v4" "go.yaml.in/yaml/v4"

View File

@@ -17,9 +17,9 @@ import (
"strings" "strings"
"sync" "sync"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/common/git" "gitea.com/gitea/act_runner/act/common/git"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
) )
func newLocalReusableWorkflowExecutor(rc *RunContext) common.Executor { func newLocalReusableWorkflowExecutor(rc *RunContext) common.Executor {

View File

@@ -23,10 +23,10 @@ import (
"strings" "strings"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"gitea.com/gitea/runner/act/exprparser" "gitea.com/gitea/act_runner/act/exprparser"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux"
@@ -383,7 +383,7 @@ func (rc *RunContext) startJobContainer() common.Executor {
if createAndDeleteNetwork { if createAndDeleteNetwork {
// clean network if it has been created by act // clean network if it has been created by act
// if using service containers // if using service containers
// it means that the network to which containers are connecting is created by `runner`, // it means that the network to which containers are connecting is created by `act_runner`,
// so, we should remove the network at last. // so, we should remove the network at last.
logger.Infof("Cleaning up network for job %s, and network name is: %s", rc.JobName, networkName) logger.Infof("Cleaning up network for job %s, and network name is: %s", rc.JobName, networkName)
if err := container.NewDockerNetworkRemoveExecutor(networkName)(ctx); err != nil { if err := container.NewDockerNetworkRemoveExecutor(networkName)(ctx); err != nil {

View File

@@ -12,8 +12,8 @@ import (
"strings" "strings"
"testing" "testing"
"gitea.com/gitea/runner/act/exprparser" "gitea.com/gitea/act_runner/act/exprparser"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
assert "github.com/stretchr/testify/assert" assert "github.com/stretchr/testify/assert"

View File

@@ -13,8 +13,8 @@ import (
"sync" "sync"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
docker_container "github.com/docker/docker/api/types/container" docker_container "github.com/docker/docker/api/types/container"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"

View File

@@ -16,8 +16,8 @@ import (
"strings" "strings"
"testing" "testing"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/joho/godotenv" "github.com/joho/godotenv"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"

View File

@@ -13,10 +13,10 @@ import (
"strings" "strings"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"gitea.com/gitea/runner/act/exprparser" "gitea.com/gitea/act_runner/act/exprparser"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
) )
type step interface { type step interface {

View File

@@ -15,8 +15,8 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
) )
type stepActionLocal struct { type stepActionLocal struct {

View File

@@ -12,8 +12,8 @@ import (
"strings" "strings"
"testing" "testing"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"

View File

@@ -16,9 +16,9 @@ import (
"regexp" "regexp"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/common/git" "gitea.com/gitea/act_runner/act/common/git"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
gogit "github.com/go-git/go-git/v5" gogit "github.com/go-git/go-git/v5"
) )

View File

@@ -14,9 +14,9 @@ import (
"testing" "testing"
"time" "time"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/common/git" "gitea.com/gitea/act_runner/act/common/git"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"

View File

@@ -9,9 +9,9 @@ import (
"fmt" "fmt"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/kballard/go-shellquote" "github.com/kballard/go-shellquote"
) )

View File

@@ -10,8 +10,8 @@ import (
"io" "io"
"testing" "testing"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"

View File

@@ -7,7 +7,7 @@ package runner
import ( import (
"fmt" "fmt"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
) )
type stepFactory interface { type stepFactory interface {

View File

@@ -7,7 +7,7 @@ package runner
import ( import (
"testing" "testing"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )

View File

@@ -11,10 +11,10 @@ import (
"runtime" "runtime"
"strings" "strings"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"gitea.com/gitea/runner/act/lookpath" "gitea.com/gitea/act_runner/act/lookpath"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/kballard/go-shellquote" "github.com/kballard/go-shellquote"
) )

View File

@@ -10,8 +10,8 @@ import (
"io" "io"
"testing" "testing"
"gitea.com/gitea/runner/act/container" "gitea.com/gitea/act_runner/act/container"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"

View File

@@ -8,8 +8,8 @@ import (
"context" "context"
"testing" "testing"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"

View File

@@ -1,4 +1,4 @@
# Usage Examples for `runner` # Usage Examples for `act_runner`
Welcome to our collection of usage and deployment examples specifically designed for Gitea setups. Whether you're a beginner or an experienced user, you'll find practical resources here that you can directly apply to enhance your Gitea experience. We encourage you to contribute your own insights and knowledge to make this collection even more comprehensive and valuable. Welcome to our collection of usage and deployment examples specifically designed for Gitea setups. Whether you're a beginner or an experienced user, you'll find practical resources here that you can directly apply to enhance your Gitea experience. We encourage you to contribute your own insights and knowledge to make this collection even more comprehensive and valuable.

View File

@@ -1,4 +1,4 @@
### Running `runner` using `docker-compose` ### Running `act_runner` using `docker-compose`
```yml ```yml
... ...
@@ -19,7 +19,7 @@
# - GITEA_RUNNER_REGISTRATION_TOKEN=<user-defined registration token> # - GITEA_RUNNER_REGISTRATION_TOKEN=<user-defined registration token>
runner: runner:
image: gitea/runner image: gitea/act_runner
restart: always restart: always
depends_on: depends_on:
gitea: gitea:
@@ -27,7 +27,7 @@
condition: service_healthy condition: service_healthy
restart: true restart: true
volumes: volumes:
- ./data/runner:/data - ./data/act_runner:/data
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
environment: environment:
- GITEA_INSTANCE_URL=<instance url> - GITEA_INSTANCE_URL=<instance url>
@@ -38,18 +38,18 @@
- GITEA_RUNNER_REGISTRATION_TOKEN=<registration token> - GITEA_RUNNER_REGISTRATION_TOKEN=<registration token>
``` ```
### Running `runner` using Docker-in-Docker (DIND) ### Running `act_runner` using Docker-in-Docker (DIND)
```yml ```yml
... ...
runner: runner:
image: gitea/runner:latest-dind-rootless image: gitea/act_runner:latest-dind-rootless
restart: always restart: always
privileged: true privileged: true
depends_on: depends_on:
- gitea - gitea
volumes: volumes:
- ./data/runner:/data - ./data/act_runner:/data
environment: environment:
- GITEA_INSTANCE_URL=<instance url> - GITEA_INSTANCE_URL=<instance url>
- DOCKER_HOST=unix:///var/run/user/1000/docker.sock - DOCKER_HOST=unix:///var/run/user/1000/docker.sock

View File

@@ -1,7 +1,7 @@
### Run `runner` in a Docker Container ### Run `act_runner` in a Docker Container
```sh ```sh
docker run -e GITEA_INSTANCE_URL=http://192.168.8.18:3000 -e GITEA_RUNNER_REGISTRATION_TOKEN=<runner_token> -v /var/run/docker.sock:/var/run/docker.sock -v $PWD/data:/data --name my_runner gitea/runner:nightly docker run -e GITEA_INSTANCE_URL=http://192.168.8.18:3000 -e GITEA_RUNNER_REGISTRATION_TOKEN=<runner_token> -v /var/run/docker.sock:/var/run/docker.sock -v $PWD/data:/data --name my_runner gitea/act_runner:nightly
``` ```
The `/data` directory inside the docker container contains the runner API keys after registration. The `/data` directory inside the docker container contains the runner API keys after registration.

View File

@@ -1,4 +1,4 @@
## Kubernetes Docker in Docker Deployment with `runner` ## Kubernetes Docker in Docker Deployment with `act_runner`
NOTE: Docker in Docker (dind) requires elevated privileges on Kubernetes. The current way to achieve this is to set the pod `SecurityContext` to `privileged`. Keep in mind that this is a potential security issue that has the potential for a malicious application to break out of the container context. NOTE: Docker in Docker (dind) requires elevated privileges on Kubernetes. The current way to achieve this is to set the pod `SecurityContext` to `privileged`. Keep in mind that this is a potential security issue that has the potential for a malicious application to break out of the container context.

View File

@@ -1,7 +1,7 @@
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
apiVersion: v1 apiVersion: v1
metadata: metadata:
name: runner-vol name: act-runner-vol
spec: spec:
accessModes: accessModes:
- ReadWriteOnce - ReadWriteOnce
@@ -25,19 +25,19 @@ apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
app: runner app: act-runner
name: runner name: act-runner
spec: spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: runner app: act-runner
strategy: {} strategy: {}
template: template:
metadata: metadata:
creationTimestamp: null creationTimestamp: null
labels: labels:
app: runner app: act-runner
spec: spec:
restartPolicy: Always restartPolicy: Always
volumes: volumes:
@@ -45,10 +45,10 @@ spec:
emptyDir: {} emptyDir: {}
- name: runner-data - name: runner-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: runner-vol claimName: act-runner-vol
containers: containers:
- name: runner - name: runner
image: gitea/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 -- 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

View File

@@ -1,7 +1,7 @@
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
apiVersion: v1 apiVersion: v1
metadata: metadata:
name: runner-vol name: act-runner-vol
spec: spec:
accessModes: accessModes:
- ReadWriteOnce - ReadWriteOnce
@@ -25,32 +25,32 @@ apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
app: runner app: act-runner
name: runner name: act-runner
spec: spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: runner app: act-runner
strategy: {} strategy: {}
template: template:
metadata: metadata:
creationTimestamp: null creationTimestamp: null
labels: labels:
app: runner app: act-runner
spec: spec:
restartPolicy: Always restartPolicy: Always
volumes: volumes:
- name: runner-data - name: runner-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: runner-vol claimName: act-runner-vol
securityContext: securityContext:
fsGroup: 1000 fsGroup: 1000
containers: containers:
- name: runner - name: runner
image: gitea/runner:nightly-dind-rootless image: gitea/act_runner:nightly-dind-rootless
imagePullPolicy: Always imagePullPolicy: Always
# command: ["sh", "-c", "while ! nc -z localhost 2376 </dev/null; do echo 'waiting for docker daemon...'; sleep 5; done; /sbin/tini -- run.sh"] # 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"]
env: env:
- name: DOCKER_HOST - name: DOCKER_HOST
value: tcp://localhost:2376 value: tcp://localhost:2376

View File

@@ -1,4 +1,4 @@
## `runner` on Virtual or Physical Servers ## `act_runner` on Virtual or Physical Servers
Files in this directory: Files in this directory:

View File

@@ -1,12 +1,12 @@
## Using Rootless Docker with`runner` ## Using Rootless Docker with`act_runner`
Here is a simple example of how to set up `runner` with rootless Docker. It has been created with Debian, but other Linux should work the same way. Here is a simple example of how to set up `act_runner` with rootless Docker. It has been created with Debian, but other Linux should work the same way.
Note: This procedure needs a real login shell -- using `sudo su` or other method of accessing the account will fail some of the steps below. Note: This procedure needs a real login shell -- using `sudo su` or other method of accessing the account will fail some of the steps below.
As `root`: As `root`:
- Create a user to run both `docker` and `runner`. In this example, we use a non-privileged account called `rootless`. - Create a user to run both `docker` and `act_runner`. In this example, we use a non-privileged account called `rootless`.
```bash ```bash
useradd -m rootless useradd -m rootless
@@ -38,36 +38,36 @@ 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.
- Create a directory for saving `runner` data between restarts - Create a directory for saving `act_runner` data between restarts
`mkdir /home/rootless/runner` `mkdir /home/rootless/act_runner`
- Register the runner from the data directory - Register the runner from the data directory
```bash ```bash
cd /home/rootless/runner cd /home/rootless/act_runner
runner register act_runner register
``` ```
- Generate a `runner` configuration file in the data directory. Edit the file to adjust for the system. - Generate a `act_runner` configuration file in the data directory. Edit the file to adjust for the system.
```bash ```bash
runner generate-config >/home/rootless/runner/config act_runner generate-config >/home/rootless/act_runner/config
``` ```
- Create a new user-level`systemd` unit file as `/home/rootless/.config/systemd/user/runner.service` with the following contents: - Create a new user-level`systemd` unit file as `/home/rootless/.config/systemd/user/act_runner.service` with the following contents:
```bash ```bash
Description=Gitea Actions runner Description=Gitea Actions runner
Documentation=https://gitea.com/gitea/runner Documentation=https://gitea.com/gitea/act_runner
After=docker.service After=docker.service
[Service] [Service]
Environment=PATH=/home/rootless/bin:/sbin:/usr/sbin:/home/rootless/bin:/home/rootless/bin:/home/rootless/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games Environment=PATH=/home/rootless/bin:/sbin:/usr/sbin:/home/rootless/bin:/home/rootless/bin:/home/rootless/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
Environment=DOCKER_HOST=unix:///run/user/1001/docker.sock Environment=DOCKER_HOST=unix:///run/user/1001/docker.sock
ExecStart=/usr/bin/runner daemon -c /home/rootless/runner/config ExecStart=/usr/bin/act_runner daemon -c /home/rootless/act_runner/config
ExecReload=/bin/kill -s HUP $MAINPID ExecReload=/bin/kill -s HUP $MAINPID
WorkingDirectory=/home/rootless/runner WorkingDirectory=/home/rootless/act_runner
TimeoutSec=0 TimeoutSec=0
RestartSec=2 RestartSec=2
Restart=always Restart=always
@@ -88,8 +88,8 @@ export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
- Reboot - Reboot
After the system restarts, check that the`runner` is working and that the runner is connected to Gitea. After the system restarts, check that the`act_runner` is working and that the runner is connected to Gitea.
````bash ````bash
systemctl --user status runner systemctl --user status act_runner
journalctl --user -xeu runner journalctl --user -xeu act_runner

6
go.mod
View File

@@ -1,10 +1,10 @@
module gitea.com/gitea/runner module gitea.com/gitea/act_runner
go 1.26.0 go 1.26.0
require ( require (
code.gitea.io/actions-proto-go v0.4.1 code.gitea.io/actions-proto-go v0.4.1
connectrpc.com/connect v1.19.1 connectrpc.com/connect v1.19.2
github.com/avast/retry-go/v4 v4.7.0 github.com/avast/retry-go/v4 v4.7.0
github.com/docker/docker v25.0.13+incompatible github.com/docker/docker v25.0.13+incompatible
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
@@ -24,7 +24,7 @@ require (
github.com/Masterminds/semver v1.5.0 github.com/Masterminds/semver v1.5.0
github.com/creack/pty v1.1.24 github.com/creack/pty v1.1.24
github.com/distribution/reference v0.6.0 github.com/distribution/reference v0.6.0
github.com/docker/cli v25.0.3+incompatible github.com/docker/cli v25.0.7+incompatible
github.com/docker/go-connections v0.6.0 github.com/docker/go-connections v0.6.0
github.com/go-git/go-billy/v5 v5.8.0 github.com/go-git/go-billy/v5 v5.8.0
github.com/go-git/go-git/v5 v5.18.0 github.com/go-git/go-git/v5 v5.18.0

4
go.sum
View File

@@ -2,6 +2,8 @@ code.gitea.io/actions-proto-go v0.4.1 h1:l0EYhjsgpUe/1VABo2eK7zcoNX2W44WOnb0MSLr
code.gitea.io/actions-proto-go v0.4.1/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= code.gitea.io/actions-proto-go v0.4.1/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
connectrpc.com/connect v1.19.2 h1:McQ83FGdzL+t60peksi0gXC7MQ/iLKgLduAnThbM0mo=
connectrpc.com/connect v1.19.2/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
cyphar.com/go-pathrs v0.2.3 h1:0pH8gep37wB0BgaXrEaN1OtZhUMeS7VvaejSr6i822o= cyphar.com/go-pathrs v0.2.3 h1:0pH8gep37wB0BgaXrEaN1OtZhUMeS7VvaejSr6i822o=
cyphar.com/go-pathrs v0.2.3/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= cyphar.com/go-pathrs v0.2.3/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
@@ -53,6 +55,8 @@ github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v25.0.3+incompatible h1:KLeNs7zws74oFuVhgZQ5ONGZiXUUdgsdy6/EsX/6284= github.com/docker/cli v25.0.3+incompatible h1:KLeNs7zws74oFuVhgZQ5ONGZiXUUdgsdy6/EsX/6284=
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/cli v25.0.7+incompatible h1:scW/AbGafKmANsonsFckFHTwpz2QypoPA/zpoLnDs/E=
github.com/docker/cli v25.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v25.0.13+incompatible h1:YeBrkUd3q0ZoRDNoEzuopwCLU+uD8GZahDHwBdsTnkU= github.com/docker/docker v25.0.13+incompatible h1:YeBrkUd3q0ZoRDNoEzuopwCLU+uD8GZahDHwBdsTnkU=
github.com/docker/docker v25.0.13+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.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=

View File

@@ -8,8 +8,8 @@ import (
"os" "os"
"os/signal" "os/signal"
"gitea.com/gitea/runner/act/artifactcache" "gitea.com/gitea/act_runner/act/artifactcache"
"gitea.com/gitea/runner/internal/pkg/config" "gitea.com/gitea/act_runner/internal/pkg/config"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"

View File

@@ -8,17 +8,17 @@ import (
"fmt" "fmt"
"os" "os"
"gitea.com/gitea/runner/internal/pkg/config" "gitea.com/gitea/act_runner/internal/pkg/config"
"gitea.com/gitea/runner/internal/pkg/ver" "gitea.com/gitea/act_runner/internal/pkg/ver"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func Execute(ctx context.Context) { func Execute(ctx context.Context) {
// ./runner // ./act_runner
rootCmd := &cobra.Command{ rootCmd := &cobra.Command{
Use: "runner [event name to run]\nIf no event name passed, will default to \"on: push\"", Use: "act_runner [event name to run]\nIf no event name passed, will default to \"on: push\"",
Short: "Gitea Runner", Short: "Run GitHub actions locally by specifying the event name (e.g. `push`) or an action name directly.",
Args: cobra.MaximumNArgs(1), Args: cobra.MaximumNArgs(1),
Version: ver.Version(), Version: ver.Version(),
SilenceUsage: true, SilenceUsage: true,
@@ -26,7 +26,7 @@ func Execute(ctx context.Context) {
configFile := "" configFile := ""
rootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "Config file path") rootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "Config file path")
// ./runner register // ./act_runner register
var regArgs registerArgs var regArgs registerArgs
registerCmd := &cobra.Command{ registerCmd := &cobra.Command{
Use: "register", Use: "register",
@@ -42,7 +42,7 @@ func Execute(ctx context.Context) {
registerCmd.Flags().BoolVar(&regArgs.Ephemeral, "ephemeral", false, "Configure the runner to be ephemeral and only ever be able to pick a single job (stricter than --once)") registerCmd.Flags().BoolVar(&regArgs.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)
// ./runner daemon // ./act_runner daemon
var daemArgs daemonArgs var daemArgs daemonArgs
daemonCmd := &cobra.Command{ daemonCmd := &cobra.Command{
Use: "daemon", Use: "daemon",
@@ -53,10 +53,10 @@ func Execute(ctx context.Context) {
daemonCmd.Flags().BoolVar(&daemArgs.Once, "once", false, "Run one job then exit") daemonCmd.Flags().BoolVar(&daemArgs.Once, "once", false, "Run one job then exit")
rootCmd.AddCommand(daemonCmd) rootCmd.AddCommand(daemonCmd)
// ./runner exec // ./act_runner exec
rootCmd.AddCommand(loadExecCmd(ctx)) rootCmd.AddCommand(loadExecCmd(ctx))
// ./runner config // ./act_runner config
rootCmd.AddCommand(&cobra.Command{ rootCmd.AddCommand(&cobra.Command{
Use: "generate-config", Use: "generate-config",
Short: "Generate an example config file", Short: "Generate an example config file",
@@ -66,7 +66,7 @@ func Execute(ctx context.Context) {
}, },
}) })
// ./runner cache-server // ./act_runner cache-server
var cacheArgs cacheServerArgs var cacheArgs cacheServerArgs
cacheCmd := &cobra.Command{ cacheCmd := &cobra.Command{
Use: "cache-server", Use: "cache-server",

View File

@@ -16,14 +16,14 @@ import (
"strings" "strings"
"time" "time"
"gitea.com/gitea/runner/internal/app/poll" "gitea.com/gitea/act_runner/internal/app/poll"
"gitea.com/gitea/runner/internal/app/run" "gitea.com/gitea/act_runner/internal/app/run"
"gitea.com/gitea/runner/internal/pkg/client" "gitea.com/gitea/act_runner/internal/pkg/client"
"gitea.com/gitea/runner/internal/pkg/config" "gitea.com/gitea/act_runner/internal/pkg/config"
"gitea.com/gitea/runner/internal/pkg/envcheck" "gitea.com/gitea/act_runner/internal/pkg/envcheck"
"gitea.com/gitea/runner/internal/pkg/labels" "gitea.com/gitea/act_runner/internal/pkg/labels"
"gitea.com/gitea/runner/internal/pkg/metrics" "gitea.com/gitea/act_runner/internal/pkg/metrics"
"gitea.com/gitea/runner/internal/pkg/ver" "gitea.com/gitea/act_runner/internal/pkg/ver"
"connectrpc.com/connect" "connectrpc.com/connect"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
@@ -104,7 +104,7 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
} }
// if dockerSocketPath passes the check, override DOCKER_HOST with dockerSocketPath // if dockerSocketPath passes the check, override DOCKER_HOST with dockerSocketPath
os.Setenv("DOCKER_HOST", dockerSocketPath) os.Setenv("DOCKER_HOST", dockerSocketPath)
// empty cfg.Container.DockerHost means runner need to find an available docker host automatically // empty cfg.Container.DockerHost means act_runner need to find an available docker host automatically
// and assign the path to cfg.Container.DockerHost // and assign the path to cfg.Container.DockerHost
if cfg.Container.DockerHost == "" { if cfg.Container.DockerHost == "" {
cfg.Container.DockerHost = dockerSocketPath cfg.Container.DockerHost = dockerSocketPath

View File

@@ -15,11 +15,11 @@ import (
"strings" "strings"
"time" "time"
"gitea.com/gitea/runner/act/artifactcache" "gitea.com/gitea/act_runner/act/artifactcache"
"gitea.com/gitea/runner/act/artifacts" "gitea.com/gitea/act_runner/act/artifacts"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"gitea.com/gitea/runner/act/runner" "gitea.com/gitea/act_runner/act/runner"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/joho/godotenv" "github.com/joho/godotenv"

View File

@@ -14,10 +14,10 @@ import (
"strings" "strings"
"time" "time"
"gitea.com/gitea/runner/internal/pkg/client" "gitea.com/gitea/act_runner/internal/pkg/client"
"gitea.com/gitea/runner/internal/pkg/config" "gitea.com/gitea/act_runner/internal/pkg/config"
"gitea.com/gitea/runner/internal/pkg/labels" "gitea.com/gitea/act_runner/internal/pkg/labels"
"gitea.com/gitea/runner/internal/pkg/ver" "gitea.com/gitea/act_runner/internal/pkg/ver"
pingv1 "code.gitea.io/actions-proto-go/ping/v1" pingv1 "code.gitea.io/actions-proto-go/ping/v1"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"

View File

@@ -12,9 +12,9 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"gitea.com/gitea/runner/internal/pkg/client" "gitea.com/gitea/act_runner/internal/pkg/client"
"gitea.com/gitea/runner/internal/pkg/config" "gitea.com/gitea/act_runner/internal/pkg/config"
"gitea.com/gitea/runner/internal/pkg/metrics" "gitea.com/gitea/act_runner/internal/pkg/metrics"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"connectrpc.com/connect" "connectrpc.com/connect"

View File

@@ -11,8 +11,8 @@ import (
"testing" "testing"
"time" "time"
"gitea.com/gitea/runner/internal/pkg/client/mocks" "gitea.com/gitea/act_runner/internal/pkg/client/mocks"
"gitea.com/gitea/runner/internal/pkg/config" "gitea.com/gitea/act_runner/internal/pkg/config"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
connect_go "connectrpc.com/connect" connect_go "connectrpc.com/connect"

View File

@@ -15,16 +15,16 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"gitea.com/gitea/runner/act/artifactcache" "gitea.com/gitea/act_runner/act/artifactcache"
"gitea.com/gitea/runner/act/common" "gitea.com/gitea/act_runner/act/common"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
"gitea.com/gitea/runner/act/runner" "gitea.com/gitea/act_runner/act/runner"
"gitea.com/gitea/runner/internal/pkg/client" "gitea.com/gitea/act_runner/internal/pkg/client"
"gitea.com/gitea/runner/internal/pkg/config" "gitea.com/gitea/act_runner/internal/pkg/config"
"gitea.com/gitea/runner/internal/pkg/labels" "gitea.com/gitea/act_runner/internal/pkg/labels"
"gitea.com/gitea/runner/internal/pkg/metrics" "gitea.com/gitea/act_runner/internal/pkg/metrics"
"gitea.com/gitea/runner/internal/pkg/report" "gitea.com/gitea/act_runner/internal/pkg/report"
"gitea.com/gitea/runner/internal/pkg/ver" "gitea.com/gitea/act_runner/internal/pkg/ver"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"connectrpc.com/connect" "connectrpc.com/connect"

View File

@@ -9,7 +9,7 @@ import (
"sort" "sort"
"strings" "strings"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"go.yaml.in/yaml/v4" "go.yaml.in/yaml/v4"

View File

@@ -6,7 +6,7 @@ package run
import ( import (
"testing" "testing"
"gitea.com/gitea/runner/act/model" "gitea.com/gitea/act_runner/act/model"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

View File

@@ -1,7 +1,7 @@
# Example configuration file, it's safe to copy this as the default config file without any modification. # Example configuration file, it's safe to copy this as the default config file without any modification.
# You don't have to copy this file to your instance, # You don't have to copy this file to your instance,
# just run `./runner generate-config > config.yaml` to generate a config file. # just run `./act_runner generate-config > config.yaml` to generate a config file.
log: log:
# The level of logging, can be trace, debug, info, warn, error, fatal # The level of logging, can be trace, debug, info, warn, error, fatal
@@ -79,21 +79,21 @@ cache:
# 0 means to use a random available port. # 0 means to use a random available port.
port: 0 port: 0
# The external cache server URL. Valid only when enable is true. # The external cache server URL. Valid only when enable is true.
# If it's specified, runner will use this URL as the ACTIONS_CACHE_URL rather than start a server by itself. # If it's specified, act_runner will use this URL as the ACTIONS_CACHE_URL rather than start a server by itself.
# The URL should generally end with "/". # The URL should generally end with "/".
external_server: "" external_server: ""
container: container:
# Specifies the network to which the container will connect. # Specifies the network to which the container will connect.
# Could be host, bridge or the name of a custom network. # Could be host, bridge or the name of a custom network.
# If it's empty, runner will create a network automatically. # If it's empty, act_runner will create a network automatically.
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
# Any other options to be used when the container is started (e.g., --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 runner will add it automatically. # NOTE: There is no need to add the first '/' of the path as act_runner will add it automatically.
# If the path starts with '/', the '/' will be trimmed. # If the path starts with '/', the '/' will be trimmed.
# For example, if the parent directory is /path/to/my/dir, workdir_parent should be path/to/my/dir # For example, if the parent directory is /path/to/my/dir, workdir_parent should be path/to/my/dir
# If it's empty, /workspace will be used. # If it's empty, /workspace will be used.
@@ -109,17 +109,17 @@ container:
# - '**' # - '**'
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, runner will find an available docker host automatically. # If it's empty, act_runner will find an available docker host automatically.
# If it's "-", 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: true 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 runner # Always require a reachable docker daemon, even if not required by act_runner
require_docker: false require_docker: false
# Timeout to wait for the docker daemon to be reachable, if docker is required by require_docker or runner # Timeout to wait for the docker daemon to be reachable, if docker is required by require_docker or act_runner
docker_timeout: 0s docker_timeout: 0s
# Bind the workspace to the host filesystem instead of using Docker volumes. # Bind the workspace to the host filesystem instead of using Docker volumes.
# This is required for Docker-in-Docker (DinD) setups when jobs use docker compose # This is required for Docker-in-Docker (DinD) setups when jobs use docker compose

View File

@@ -60,8 +60,8 @@ type Container struct {
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 runner 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 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
BindWorkdir bool `yaml:"bind_workdir"` // BindWorkdir binds the workspace to the host filesystem instead of using Docker volumes. Required for DinD when jobs use docker compose with bind mounts. BindWorkdir bool `yaml:"bind_workdir"` // BindWorkdir binds the workspace to the host filesystem instead of using Docker volumes. Required for DinD when jobs use docker compose with bind mounts.
} }
@@ -185,7 +185,7 @@ func LoadDefault(file string) (*Config, error) {
if cfg.Container.NetworkMode == "bridge" { if cfg.Container.NetworkMode == "bridge" {
// Previously, if the value of `container.network_mode` is `bridge`, we will create a new network for job. // Previously, if the value of `container.network_mode` is `bridge`, we will create a new network for job.
// But “bridge” is easily confused with the bridge network created by Docker by default. // But “bridge” is easily confused with the bridge network created by Docker by default.
// So we set the value of `container.network` to empty string to make `runner` automatically create a new network for job. // So we set the value of `container.network` to empty string to make `act_runner` automatically create a new network for job.
cfg.Container.Network = "" cfg.Container.Network = ""
} else { } else {
cfg.Container.Network = cfg.Container.NetworkMode cfg.Container.Network = cfg.Container.NetworkMode

View File

@@ -8,7 +8,7 @@ import (
"os" "os"
) )
const registrationWarning = "This file is automatically generated by Gitea Runner. Do not edit it manually unless you know what you are doing. Removing this file will cause Gitea Runner to re-register as a new runner." const registrationWarning = "This file is automatically generated by act-runner. Do not edit it manually unless you know what you are doing. Removing this file will cause act runner to re-register as a new runner."
// Registration is the registration information for a runner // Registration is the registration information for a runner
type Registration struct { type Registration struct {

View File

@@ -12,8 +12,8 @@ import (
"github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/collectors"
) )
// Namespace is the Prometheus namespace for all runner metrics. // Namespace is the Prometheus namespace for all act_runner metrics.
const Namespace = "runner" const Namespace = "act_runner"
// Label value constants for Prometheus metrics. // Label value constants for Prometheus metrics.
// Using constants prevents typos from silently creating new time-series. // Using constants prevents typos from silently creating new time-series.

View File

@@ -10,11 +10,12 @@ import (
"regexp" "regexp"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"gitea.com/gitea/runner/internal/pkg/client" "gitea.com/gitea/act_runner/internal/pkg/client"
"gitea.com/gitea/runner/internal/pkg/config" "gitea.com/gitea/act_runner/internal/pkg/config"
"gitea.com/gitea/runner/internal/pkg/metrics" "gitea.com/gitea/act_runner/internal/pkg/metrics"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"connectrpc.com/connect" "connectrpc.com/connect"
@@ -48,6 +49,10 @@ type Reporter struct {
outputs sync.Map outputs sync.Map
daemon chan struct{} daemon chan struct{}
// Unix-nanos of the last successful UpdateTask. Atomic so the heartbeat
// guard in ReportState reads it without contending stateMu.
lastReportedAtNanos atomic.Int64
// Adaptive batching control // Adaptive batching control
logReportInterval time.Duration logReportInterval time.Duration
logReportMaxLatency time.Duration logReportMaxLatency time.Duration
@@ -489,8 +494,12 @@ func (r *Reporter) ReportState(reportResult bool) error {
// Consume stateChanged atomically with the snapshot; restored on error // Consume stateChanged atomically with the snapshot; restored on error
// below so a concurrent Fire() during UpdateTask isn't silently lost. // below so a concurrent Fire() during UpdateTask isn't silently lost.
// Heartbeat at stateReportInterval even when nothing changed, so the server
// doesn't time out long-running silent jobs as orphaned (#826).
last := r.lastReportedAtNanos.Load()
withinHeartbeatInterval := last != 0 && time.Since(time.Unix(0, last)) < r.stateReportInterval
r.stateMu.Lock() r.stateMu.Lock()
if !reportResult && !r.stateChanged && len(outputs) == 0 { if !reportResult && !r.stateChanged && len(outputs) == 0 && withinHeartbeatInterval {
r.stateMu.Unlock() r.stateMu.Unlock()
return nil return nil
} }
@@ -517,6 +526,7 @@ func (r *Reporter) ReportState(reportResult bool) error {
return err return err
} }
metrics.ReportStateTotal.WithLabelValues(metrics.LabelResultSuccess).Inc() metrics.ReportStateTotal.WithLabelValues(metrics.LabelResultSuccess).Inc()
r.lastReportedAtNanos.Store(time.Now().UnixNano())
for _, k := range resp.Msg.SentOutputs { for _, k := range resp.Msg.SentOutputs {
r.outputs.Store(k, struct{}{}) r.outputs.Store(k, struct{}{})

View File

@@ -12,8 +12,8 @@ import (
"testing" "testing"
"time" "time"
"gitea.com/gitea/runner/internal/pkg/client/mocks" "gitea.com/gitea/act_runner/internal/pkg/client/mocks"
"gitea.com/gitea/runner/internal/pkg/config" "gitea.com/gitea/act_runner/internal/pkg/config"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
connect_go "connectrpc.com/connect" connect_go "connectrpc.com/connect"
@@ -220,7 +220,7 @@ func TestReporter_Fire(t *testing.T) {
} }
// TestReporter_EphemeralRunnerDeletion reproduces the exact scenario from // TestReporter_EphemeralRunnerDeletion reproduces the exact scenario from
// https://gitea.com/gitea/runner/issues/793: // https://gitea.com/gitea/act_runner/issues/793:
// //
// 1. RunDaemon calls ReportLog(false) — runner is still alive // 1. RunDaemon calls ReportLog(false) — runner is still alive
// 2. Close() updates state to Result=FAILURE (between RunDaemon's ReportLog and ReportState) // 2. Close() updates state to Result=FAILURE (between RunDaemon's ReportLog and ReportState)
@@ -597,3 +597,45 @@ func TestReporter_StateNotifyFlush(t *testing.T) {
}, 500*time.Millisecond, 10*time.Millisecond, }, 500*time.Millisecond, 10*time.Millisecond,
"step transition should have triggered immediate state flush via stateNotify") "step transition should have triggered immediate state flush via stateNotify")
} }
// TestReporter_StateHeartbeat verifies that ReportState sends a heartbeat
// UpdateTask once stateReportInterval has elapsed since the last successful
// report, even when nothing has changed. Without this, long-running silent
// jobs (no log output, no step transitions) cause the server to time the
// task out and cancel it (#826).
func TestReporter_StateHeartbeat(t *testing.T) {
var updateTaskCalls atomic.Int64
client := mocks.NewClient(t)
client.On("UpdateTask", mock.Anything, mock.Anything).Return(
func(_ context.Context, _ *connect_go.Request[runnerv1.UpdateTaskRequest]) (*connect_go.Response[runnerv1.UpdateTaskResponse], error) {
updateTaskCalls.Add(1)
return connect_go.NewResponse(&runnerv1.UpdateTaskResponse{}), nil
},
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
taskCtx, err := structpb.NewStruct(map[string]any{})
require.NoError(t, err)
cfg, _ := config.LoadDefault("")
cfg.Runner.StateReportInterval = 50 * time.Millisecond
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: taskCtx}, cfg)
reporter.ResetSteps(1)
// First call has no prior report — sends to seed lastReportedAt.
reporter.stateMu.Lock()
reporter.stateChanged = true
reporter.stateMu.Unlock()
require.NoError(t, reporter.ReportState(false))
require.Equal(t, int64(1), updateTaskCalls.Load())
// Second call immediately after with nothing changed — must skip.
require.NoError(t, reporter.ReportState(false))
assert.Equal(t, int64(1), updateTaskCalls.Load(), "no-op ReportState within stateReportInterval must skip")
// After stateReportInterval elapses, a heartbeat must fire even with no changes.
time.Sleep(2 * cfg.Runner.StateReportInterval)
require.NoError(t, reporter.ReportState(false))
assert.Equal(t, int64(2), updateTaskCalls.Load(), "ReportState must heartbeat after stateReportInterval even with no state change")
}

View File

@@ -3,7 +3,7 @@
package ver package ver
// go build -ldflags "-X gitea.com/gitea/runner/internal/pkg/ver.version=1.2.3" // go build -ldflags "-X gitea.com/gitea/act_runner/internal/pkg/ver.version=1.2.3"
var version = "dev" var version = "dev"
func Version() string { func Version() string {

View File

@@ -8,7 +8,7 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"gitea.com/gitea/runner/internal/app/cmd" "gitea.com/gitea/act_runner/internal/app/cmd"
) )
func main() { func main() {

View File

@@ -36,12 +36,12 @@ if [[ ! -s "$RUNNER_STATE_FILE" ]]; then
try=$((try + 1)) try=$((try + 1))
success=0 success=0
# The point of this loop is to make it simple, when running both runner and gitea in docker, # The point of this loop is to make it simple, when running both act_runner and gitea in docker,
# for the runner to wait a moment for gitea to become available before erroring out. Within # for the act_runner to wait a moment for gitea to become available before erroring out. Within
# the context of a single docker-compose, something similar could be done via healthchecks, but # the context of a single docker-compose, something similar could be done via healthchecks, but
# this is more flexible. # this is more flexible.
while [[ $success -eq 0 ]] && [[ $try -lt ${GITEA_MAX_REG_ATTEMPTS:-10} ]]; do while [[ $success -eq 0 ]] && [[ $try -lt ${GITEA_MAX_REG_ATTEMPTS:-10} ]]; do
runner register \ act_runner register \
--instance "${GITEA_INSTANCE_URL}" \ --instance "${GITEA_INSTANCE_URL}" \
--token "${GITEA_RUNNER_REGISTRATION_TOKEN}" \ --token "${GITEA_RUNNER_REGISTRATION_TOKEN}" \
--name "${GITEA_RUNNER_NAME:-`hostname`}" \ --name "${GITEA_RUNNER_NAME:-`hostname`}" \
@@ -57,8 +57,8 @@ if [[ ! -s "$RUNNER_STATE_FILE" ]]; then
fi fi
done done
fi fi
# Prevent reading the token from the runner process # Prevent reading the token from the act_runner process
unset GITEA_RUNNER_REGISTRATION_TOKEN unset GITEA_RUNNER_REGISTRATION_TOKEN
unset GITEA_RUNNER_REGISTRATION_TOKEN_FILE unset GITEA_RUNNER_REGISTRATION_TOKEN_FILE
exec runner daemon ${CONFIG_ARG} ${RUN_ARGS} exec act_runner daemon ${CONFIG_ARG} ${RUN_ARGS}

9
scripts/s6/act_runner/run Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
while [ ! -d /etc/s6/docker/supervise ]; do
sleep 0.1
done
s6-svwait -U /etc/s6/docker
exec run.sh

View File

@@ -1,5 +0,0 @@
#!/usr/bin/env bash
s6-svwait -U /etc/s6/docker
exec run.sh