mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-08-06 00:44:22 +02:00
feat: gate set-env/add-path and render annotation locations (#1109)
`::set-env::` and `::add-path::` let a step rewrite the environment of every later step from its own output, which the runner honoured silently. They are now refused, as GitHub has done since 2020, and `ACTIONS_ALLOW_UNSECURE_COMMANDS` opts back in per step or job. Support for that variable is new here too, and is the only opt-in, matching GitHub rather than adding a runner config key on top. Annotations keep their source location: Gitea has no annotation store and its web UI strips command properties, so `::error file=main.go,line=12::msg` is rendered as `::error::main.go:12: msg`. `DEVELOPMENT.md` writes down the log line encoding rules this relies on. --------- Co-authored-by: silverwind <me@silverwind.io> Reviewed-on: https://gitea.com/gitea/runner/pulls/1109 Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com> Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
@@ -6,6 +6,7 @@ package runner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gitea.com/gitea/runner/act/common"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
yaml "go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
@@ -354,3 +356,48 @@ func TestIsContinueOnError(t *testing.T) {
|
||||
assertObject.False(continueOnError)
|
||||
assertObject.Error(err)
|
||||
}
|
||||
|
||||
// A refused ::set-env::/::add-path:: records a job-scoped error. When the step that
|
||||
// produced it also fails on its own, the refusal must be cleared at the step boundary, so
|
||||
// it fails only that step and never leaks onto a later step that runs anyway (if: always()).
|
||||
func TestRunStepExecutorDoesNotLeakRefusalToNextStep(t *testing.T) {
|
||||
cm := &containerMock{}
|
||||
noop := func(context.Context) error { return nil }
|
||||
cm.On("Copy", mock.Anything, mock.Anything).Return(noop)
|
||||
cm.On("UpdateFromEnv", mock.Anything, mock.Anything).Return(noop)
|
||||
|
||||
rc := &RunContext{
|
||||
Config: &Config{Env: map[string]string{}},
|
||||
Run: &model.Run{
|
||||
JobID: "1",
|
||||
Workflow: &model.Workflow{Jobs: map[string]*model.Job{"1": {}}},
|
||||
},
|
||||
Env: map[string]string{},
|
||||
StepResults: map[string]*model.StepResult{},
|
||||
JobContainer: cm,
|
||||
}
|
||||
rc.ExprEval = rc.NewExpressionEvaluator(context.Background())
|
||||
// Dryrun skips reading the path file back from the (mocked) container.
|
||||
ctx := common.WithDryrun(context.Background(), true)
|
||||
|
||||
// A refusal parsed out of the job container's own output belongs to no step, so the
|
||||
// first step must not be failed by it.
|
||||
rc.commandHandler(ctx)("::set-env name=setup::y\n")
|
||||
stepSetup := &stepRun{RunContext: rc, Step: &model.Step{ID: "setup"}, env: map[string]string{}}
|
||||
require.NoError(t, runStepExecutor(stepSetup, stepStageMain, func(context.Context) error { return nil })(ctx))
|
||||
|
||||
// Step A refuses a ::set-env:: and then fails on its own.
|
||||
stepA := &stepRun{RunContext: rc, Step: &model.Step{ID: "a"}, env: map[string]string{}}
|
||||
errA := runStepExecutor(stepA, stepStageMain, func(context.Context) error {
|
||||
rc.commandHandler(ctx)("::set-env name=x::y\n")
|
||||
return errors.New("boom")
|
||||
})(ctx)
|
||||
// The step fails with its own error, not the refusal.
|
||||
require.ErrorContains(t, errA, "boom")
|
||||
|
||||
// Step B runs despite step A's failure (if: always()) and issues no unsecure command;
|
||||
// it must not inherit step A's refusal.
|
||||
stepB := &stepRun{RunContext: rc, Step: &model.Step{ID: "b", If: yaml.Node{Value: "always()"}}, env: map[string]string{}}
|
||||
errB := runStepExecutor(stepB, stepStageMain, func(context.Context) error { return nil })(ctx)
|
||||
require.NoError(t, errB)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user