Skip to content

Commit bc2fbe7

Browse files
authored
refactor(actions): read runner capabilities from proto field (#38068)
[actions-proto-go v0.6.0](https://gitea.com/gitea/actions-proto-go) adds a `capabilities` field to `RegisterRequest` and `DeclareRequest`. This lets a runner advertise the transitional `cancelling` capability directly in the proto message instead of through the out-of-band mechanism we used while the proto bump was pending. This PR: - Bumps `gitea.dev/actions-proto-go` to `v0.6.0`. - Drops the forward-compat `capabilityGetter` type-assertion shim and the `runnerRequestHasCancellingCapability` helper, reading `GetCapabilities()` directly (now part of the `declareRequest` interface). - Removes the "capability state unknown → preserve existing value" branch. ## Why the behaviour change is correct The shim and the `(hasSupport, known)` two-value return only existed because the old proto had no `capabilities` field, so we couldn't tell "runner doesn't support it" from "we can't see the field." With v0.6.0 the field is always present. Since proto3 repeated fields have no presence, "no capabilities sent" now unambiguously means the runner does not advertise the capability, so a runner that omits `cancelling` is correctly recorded as `HasCancellingSupport = false`. There is no regression: prior to this bump Gitea was on `v0.5.0`, where the type assertion always failed and `HasCancellingSupport` was therefore never set from requests — so no runner relied on the preserved-unknown path. ## Compatibility The change is wire-compatible in both directions of version skew, because the new field uses a previously unused field number (8 on `RegisterRequest`, 3 on `DeclareRequest`) and the transport uses the binary protobuf codec: - **Old runner → new Gitea:** the runner omits the field; it decodes to an empty capability list. Registration/declaration succeed; the runner simply doesn't get the cancelling feature. - **New runner → old Gitea:** the runner sends the field; the old server's generated code doesn't know the field number and silently ignores it. Registration/declaration succeed. The feature only activates once both server and runner are on `v0.6.0`.
1 parent 442f5e7 commit bc2fbe7

4 files changed

Lines changed: 28 additions & 72 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
gitea.com/go-chi/session v0.0.0-20251124165456-68e0254e989e
1313
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
1414
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
15-
gitea.dev/actions-proto-go v0.5.0
15+
gitea.dev/actions-proto-go v0.6.0
1616
gitea.dev/sdk v1.0.1
1717
github.com/42wim/httpsig v1.2.4
1818
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHq
2626
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
2727
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
2828
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
29-
gitea.dev/actions-proto-go v0.5.0 h1:Fc3DI4Fm3B3JBRXFUjegql+usoNAjjAw1cxMansfA2I=
30-
gitea.dev/actions-proto-go v0.5.0/go.mod h1:p4RX+D9oqiEEzzkPMXscw2CmaGuYFPWFc6xIOmDNDqs=
29+
gitea.dev/actions-proto-go v0.6.0 h1:gjllYQ5vmwlkqOeofTQu5qKTZpmf7kWsafoHvoPCSzY=
30+
gitea.dev/actions-proto-go v0.6.0/go.mod h1:p4RX+D9oqiEEzzkPMXscw2CmaGuYFPWFc6xIOmDNDqs=
3131
gitea.dev/sdk v1.0.1 h1:CWXQUQvp2I6YKOWkhYo1Flx2sRNfMK1X9Op4oR2awXs=
3232
gitea.dev/sdk v1.0.1/go.mod h1:jCf5Uzz0Jkb61jxNgMxLOCWwle1J1B2nKdcRtxuK9rY=
3333
github.com/42wim/httpsig v1.2.4 h1:mI5bH0nm4xn7K18fo1K3okNDRq8CCJ0KbBYWyA6r8lU=

routers/api/actions/runner/runner.go

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (s *Service) Register(
6969
}
7070

7171
labels := req.Msg.Labels
72-
hasCancellingSupport, _ := runnerRequestHasCancellingCapability(req.Msg)
72+
hasCancellingSupport := slices.Contains(req.Msg.GetCapabilities(), runnerCapabilityCancelling)
7373

7474
// create new runner
7575
name := util.EllipsisDisplayString(req.Msg.Name, 255)
@@ -116,35 +116,20 @@ func (s *Service) Register(
116116
// state and will run post-step cleanup before finalizing the task.
117117
const runnerCapabilityCancelling = "cancelling"
118118

119-
type capabilityGetter interface {
120-
GetCapabilities() []string
121-
}
122-
123119
type declareRequest interface {
124120
proto.Message
125121
GetVersion() string
126122
GetLabels() []string
127-
}
128-
129-
func runnerRequestHasCancellingCapability(req proto.Message) (bool, bool) {
130-
if req == nil {
131-
return false, false
132-
}
133-
134-
if typedReq, ok := any(req).(capabilityGetter); ok {
135-
return slices.Contains(typedReq.GetCapabilities(), runnerCapabilityCancelling), true
136-
}
137-
138-
return false, false
123+
GetCapabilities() []string
139124
}
140125

141126
func applyDeclareRequestToRunner(runner *actions_model.ActionRunner, req declareRequest) []string {
142127
runner.AgentLabels = req.GetLabels()
143128
runner.Version = req.GetVersion()
144129

145130
cols := []string{"agent_labels", "version"}
146-
hasCancellingSupport, capabilityStateKnown := runnerRequestHasCancellingCapability(req)
147-
if capabilityStateKnown && runner.HasCancellingSupport != hasCancellingSupport {
131+
hasCancellingSupport := slices.Contains(req.GetCapabilities(), runnerCapabilityCancelling)
132+
if runner.HasCancellingSupport != hasCancellingSupport {
148133
runner.HasCancellingSupport = hasCancellingSupport
149134
cols = append(cols, "has_cancelling_support")
150135
}

routers/api/actions/runner/runner_test.go

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,47 +12,22 @@ import (
1212
"github.com/stretchr/testify/assert"
1313
)
1414

15-
type capabilityRegisterRequest struct {
16-
*runnerv1.RegisterRequest
17-
capabilities []string
18-
}
19-
20-
func (r *capabilityRegisterRequest) GetCapabilities() []string {
21-
return r.capabilities
22-
}
23-
24-
type capabilityDeclareRequest struct {
25-
*runnerv1.DeclareRequest
26-
capabilities []string
27-
}
28-
29-
func (r *capabilityDeclareRequest) GetCapabilities() []string {
30-
return r.capabilities
31-
}
32-
33-
func TestRunnerRequestHasCancellingCapabilityTypedAccessor(t *testing.T) {
34-
registerReq := &capabilityRegisterRequest{
35-
RegisterRequest: &runnerv1.RegisterRequest{},
36-
capabilities: []string{runnerCapabilityCancelling, "other"},
37-
}
38-
hasCapability, known := runnerRequestHasCancellingCapability(registerReq)
39-
assert.True(t, hasCapability)
40-
assert.True(t, known)
41-
42-
declareReq := &capabilityDeclareRequest{
43-
DeclareRequest: &runnerv1.DeclareRequest{},
44-
capabilities: nil,
15+
func TestApplyDeclareRequestToRunnerAdvertisedCapabilityEnablesCancelling(t *testing.T) {
16+
runner := &actions_model.ActionRunner{}
17+
req := &runnerv1.DeclareRequest{
18+
Version: "1.2.3",
19+
Labels: []string{"linux"},
20+
Capabilities: []string{runnerCapabilityCancelling, "other"},
4521
}
46-
hasCapability, known = runnerRequestHasCancellingCapability(declareReq)
47-
assert.False(t, hasCapability)
48-
assert.True(t, known)
4922

50-
hasCapability, known = runnerRequestHasCancellingCapability(nil)
51-
assert.False(t, hasCapability)
52-
assert.False(t, known)
23+
cols := applyDeclareRequestToRunner(runner, req)
24+
assert.Equal(t, []string{"agent_labels", "version", "has_cancelling_support"}, cols)
25+
assert.True(t, runner.HasCancellingSupport)
26+
assert.Equal(t, "1.2.3", runner.Version)
27+
assert.Equal(t, []string{"linux"}, runner.AgentLabels)
5328
}
5429

55-
func TestApplyDeclareRequestToRunnerPreservesUnknownCapabilityState(t *testing.T) {
30+
func TestApplyDeclareRequestToRunnerMissingCapabilityDisablesCancelling(t *testing.T) {
5631
runner := &actions_model.ActionRunner{
5732
HasCancellingSupport: true,
5833
}
@@ -62,25 +37,21 @@ func TestApplyDeclareRequestToRunnerPreservesUnknownCapabilityState(t *testing.T
6237
}
6338

6439
cols := applyDeclareRequestToRunner(runner, req)
65-
assert.Equal(t, []string{"agent_labels", "version"}, cols)
66-
assert.True(t, runner.HasCancellingSupport)
67-
assert.Equal(t, "1.2.3", runner.Version)
68-
assert.Equal(t, []string{"linux"}, runner.AgentLabels)
40+
assert.Equal(t, []string{"agent_labels", "version", "has_cancelling_support"}, cols)
41+
assert.False(t, runner.HasCancellingSupport)
6942
}
7043

71-
func TestApplyDeclareRequestToRunnerUpdatesTypedCapabilityState(t *testing.T) {
44+
func TestApplyDeclareRequestToRunnerUnchangedCapabilityOmitsColumn(t *testing.T) {
7245
runner := &actions_model.ActionRunner{
7346
HasCancellingSupport: true,
7447
}
75-
req := &capabilityDeclareRequest{
76-
DeclareRequest: &runnerv1.DeclareRequest{
77-
Version: "1.2.3",
78-
Labels: []string{"linux"},
79-
},
80-
capabilities: []string{},
48+
req := &runnerv1.DeclareRequest{
49+
Version: "1.2.3",
50+
Labels: []string{"linux"},
51+
Capabilities: []string{runnerCapabilityCancelling},
8152
}
8253

8354
cols := applyDeclareRequestToRunner(runner, req)
84-
assert.Equal(t, []string{"agent_labels", "version", "has_cancelling_support"}, cols)
85-
assert.False(t, runner.HasCancellingSupport)
55+
assert.Equal(t, []string{"agent_labels", "version"}, cols)
56+
assert.True(t, runner.HasCancellingSupport)
8657
}

0 commit comments

Comments
 (0)