refactor: migrate to connect-go v2 (#1325)

Migrates the runner to `connectrpc.com/connect/v2` by pinning actionslib v1.5.0, which carries https://gitea.com/gitea/actionslib/pulls/28. The wire behaviour towards Gitea is unchanged and works against Gitea servers on connect v1 and v2. The client keeps v1's unlimited response size, and the error codes and messages the runner checks stay the same.

Reviewed-on: https://gitea.com/gitea/runner/pulls/1325
Reviewed-by: bircni <[email protected]>
Co-authored-by: silverwind <[email protected]>
This commit is contained in:
silverwind authored and silverwind committed 2026-10-21 16:10:58 +00:00
1 parent 430f7bb2cf
commit 6f0d4f1b48
17 files changed
+292 -301

No files matched your search

+9 -12
View File
@@ -41,7 +41,7 @@ import (
"gitea.com/gitea/runner/internal/pkg/telemetry"
"gitea.com/gitea/runner/internal/pkg/ver"
"connectrpc.com/connect"
"connectrpc.com/connect/v2"
"gitea.dev/actionslib/pkg/model"
runnerv1 "gitea.dev/actionslib/runner/v1"
docker_container "github.com/moby/moby/api/types/container"
@@ -293,14 +293,6 @@ func (r *Runner) cleanupStaleDirs(ctx context.Context, root string, match func(n
}
}
func (r *Runner) SetCapabilitiesFromDeclare(resp *connect.Response[runnerv1.DeclareResponse]) {
if resp == nil {
return
}
// Capability negotiation is done via response headers to avoid a hard proto bump.
r.capabilities = strings.TrimSpace(resp.Header().Get("X-Gitea-Actions-Capabilities"))
}
func (r *Runner) Run(ctx context.Context, task *runnerv1.Task) error {
if _, ok := r.runningTasks.LoadOrStore(task.Id, struct{}{}); ok {
return fmt.Errorf("task %d is already running", task.Id)
@@ -851,12 +843,17 @@ func nearestExistingPath(path string) string {
return "."
}
func (r *Runner) Declare(ctx context.Context, labels []string) (*connect.Response[runnerv1.DeclareResponse], error) {
return r.client.Declare(ctx, connect.NewRequest(&runnerv1.DeclareRequest{
func (r *Runner) Declare(ctx context.Context, labels []string) (*runnerv1.DeclareResponse, error) {
ctx, info := connect.NewClientContext(ctx)
resp, err := r.client.Declare(ctx, &runnerv1.DeclareRequest{
Version: ver.Version(),
Labels: labels,
Capabilities: RunnerCapabilities(),
}))
})
if err == nil {
r.capabilities = strings.TrimSpace(info.ResponseHeader().Get("X-Gitea-Actions-Capabilities"))
}
return resp, err
}
// minFreeDisk keeps the cache from growing past the point where the runner stops taking
+18 -24
View File
@@ -25,7 +25,7 @@ import (
"gitea.com/gitea/runner/internal/pkg/labels"
"gitea.com/gitea/runner/internal/pkg/ver"
"connectrpc.com/connect"
"connectrpc.com/connect/v2"
"gitea.dev/actionslib/pkg/model"
runnerv1 "gitea.dev/actionslib/runner/v1"
"github.com/stretchr/testify/assert"
@@ -38,27 +38,21 @@ func TestRunnerCapabilitiesAndDeclare(t *testing.T) {
require.Equal(t, []string{CapabilityCancelling}, RunnerCapabilities())
cli := clientmocks.NewClient(t)
cli.On("Declare", mock.Anything, mock.MatchedBy(func(req *connect.Request[runnerv1.DeclareRequest]) bool {
return req.Msg.Version == ver.Version() &&
len(req.Msg.Labels) == 1 &&
req.Msg.Labels[0] == "ubuntu" &&
len(req.Msg.Capabilities) == 1 &&
req.Msg.Capabilities[0] == CapabilityCancelling
})).Return(connect.NewResponse(&runnerv1.DeclareResponse{}), nil)
cli.On("Declare", mock.Anything, mock.MatchedBy(func(req *runnerv1.DeclareRequest) bool {
return req.Version == ver.Version() &&
len(req.Labels) == 1 &&
req.Labels[0] == "ubuntu" &&
len(req.Capabilities) == 1 &&
req.Capabilities[0] == CapabilityCancelling
})).Return(&runnerv1.DeclareResponse{}, nil).Run(func(args mock.Arguments) {
info, ok := connect.CallInfoForClientContext(args.Get(0).(context.Context))
require.True(t, ok)
info.ResponseHeader().Set("X-Gitea-Actions-Capabilities", " cancelling,cache-v2 ")
})
r := &Runner{client: cli}
_, err := r.Declare(t.Context(), []string{"ubuntu"})
require.NoError(t, err)
}
func TestRunnerSetCapabilitiesFromDeclare(t *testing.T) {
r := &Runner{}
r.SetCapabilitiesFromDeclare(nil)
require.Empty(t, r.capabilities)
resp := connect.NewResponse(&runnerv1.DeclareResponse{})
resp.Header().Set("X-Gitea-Actions-Capabilities", " cancelling,cache-v2 ")
r.SetCapabilitiesFromDeclare(resp)
require.Equal(t, "cancelling,cache-v2", r.capabilities)
}
@@ -104,20 +98,20 @@ func TestRunnerReclaimsVolumesAfterReporting(t *testing.T) {
r := NewRunner(cfg, &config.Registration{UUID: "runner-1", Labels: []string{"ubuntu:docker://node:20"}}, cli)
var reported atomic.Bool
var removed atomic.Int64
cli.On("UpdateLog", mock.Anything, mock.Anything).Return(func(_ context.Context, req *connect.Request[runnerv1.UpdateLogRequest]) (*connect.Response[runnerv1.UpdateLogResponse], error) {
cli.On("UpdateLog", mock.Anything, mock.Anything).Return(func(_ context.Context, req *runnerv1.UpdateLogRequest) (*runnerv1.UpdateLogResponse, error) {
assert.False(t, reported.Load())
return connect.NewResponse(&runnerv1.UpdateLogResponse{AckIndex: req.Msg.Index + int64(len(req.Msg.Rows))}), nil
return &runnerv1.UpdateLogResponse{AckIndex: req.Index + int64(len(req.Rows))}, nil
})
cli.On("UpdateTask", mock.Anything, mock.Anything).Return(func(_ context.Context, req *connect.Request[runnerv1.UpdateTaskRequest]) (*connect.Response[runnerv1.UpdateTaskResponse], error) {
if req.Msg.State.Result != runnerv1.Result_RESULT_UNSPECIFIED {
assert.Equal(t, runnerv1.Result_RESULT_SUCCESS, req.Msg.State.Result)
cli.On("UpdateTask", mock.Anything, mock.Anything).Return(func(_ context.Context, req *runnerv1.UpdateTaskRequest) (*runnerv1.UpdateTaskResponse, error) {
if req.State.Result != runnerv1.Result_RESULT_UNSPECIFIED {
assert.Equal(t, runnerv1.Result_RESULT_SUCCESS, req.State.Result)
assert.Equal(t, int64(1), r.RunningCount())
if mode == "post-task script" {
assert.FileExists(t, cfg.Runner.PostTaskScript+".done")
}
reported.Store(true)
}
return connect.NewResponse(&runnerv1.UpdateTaskResponse{State: req.Msg.State}), nil
return &runnerv1.UpdateTaskResponse{State: req.State}, nil
})
var volumesCreated atomic.Bool
daemon := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {