forked from actions/act_runner
Compare commits
7 Commits
e9c297600c
...
main
Author | SHA1 | Date | |
---|---|---|---|
f58a6daa6c | |||
54308690d0 | |||
4b90fa4325 | |||
5302c25feb | |||
a616ed1a10 | |||
f0b5aff3bb | |||
44b4736703 |
2
Makefile
2
Makefile
@ -103,7 +103,7 @@ fmt-check:
|
||||
fi;
|
||||
|
||||
test: fmt-check
|
||||
@$(GO) test -v -cover -coverprofile coverage.txt ./... && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||
@$(GO) test -v -cover -coverprofile coverage.txt ./... && echo -e "\n===> \e[32mOk\e[m\n" || exit 1
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
|
@ -52,7 +52,7 @@ func runRegister(ctx context.Context, regArgs *registerArgs, configFile *string)
|
||||
}
|
||||
} else {
|
||||
go func() {
|
||||
if err := registerInteractive(ctx, *configFile); err != nil {
|
||||
if err := registerInteractive(ctx, *configFile, regArgs); err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
@ -127,6 +127,22 @@ func validateLabels(ls []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *registerInputs) stageValue(stage registerStage) string {
|
||||
switch stage {
|
||||
case StageInputInstance:
|
||||
return r.InstanceAddr
|
||||
case StageInputToken:
|
||||
return r.Token
|
||||
case StageInputRunnerName:
|
||||
return r.RunnerName
|
||||
case StageInputLabels:
|
||||
if len(r.Labels) > 0 {
|
||||
return strings.Join(r.Labels, ",")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *registerInputs) assignToNext(stage registerStage, value string, cfg *config.Config) registerStage {
|
||||
// 必须设置实例地址和令牌。
|
||||
// 如果为空,保持当前阶段。
|
||||
@ -181,6 +197,7 @@ func (r *registerInputs) assignToNext(stage registerStage, value string, cfg *co
|
||||
|
||||
if validateLabels(r.Labels) != nil {
|
||||
log.Infoln("无效的标签, 请重新输入, 留空以使用默认标签 (例如, debian-latest:lcr.loongnix.cn/library/debian:latest) ")
|
||||
r.Labels = nil
|
||||
return StageInputLabels
|
||||
}
|
||||
return StageWaitingForRegistration
|
||||
@ -188,11 +205,25 @@ func (r *registerInputs) assignToNext(stage registerStage, value string, cfg *co
|
||||
return StageUnknown
|
||||
}
|
||||
|
||||
func registerInteractive(ctx context.Context, configFile string) error {
|
||||
func initInputs(regArgs *registerArgs) *registerInputs {
|
||||
inputs := ®isterInputs{
|
||||
InstanceAddr: regArgs.InstanceAddr,
|
||||
Token: regArgs.Token,
|
||||
RunnerName: regArgs.RunnerName,
|
||||
Ephemeral: regArgs.Ephemeral,
|
||||
}
|
||||
regArgs.Labels = strings.TrimSpace(regArgs.Labels)
|
||||
// command line flag.
|
||||
if regArgs.Labels != "" {
|
||||
inputs.Labels = strings.Split(regArgs.Labels, ",")
|
||||
}
|
||||
return inputs
|
||||
}
|
||||
|
||||
func registerInteractive(ctx context.Context, configFile string, regArgs *registerArgs) error {
|
||||
var (
|
||||
reader = bufio.NewReader(os.Stdin)
|
||||
stage = StageInputInstance
|
||||
inputs = new(registerInputs)
|
||||
)
|
||||
|
||||
cfg, err := config.LoadDefault(configFile)
|
||||
@ -202,13 +233,17 @@ func registerInteractive(ctx context.Context, configFile string) error {
|
||||
if f, err := os.Stat(cfg.Runner.File); err == nil && !f.IsDir() {
|
||||
stage = StageOverwriteLocalConfig
|
||||
}
|
||||
inputs := initInputs(regArgs)
|
||||
|
||||
for {
|
||||
printStageHelp(stage)
|
||||
|
||||
cmdString, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return err
|
||||
cmdString := inputs.stageValue(stage)
|
||||
if cmdString == "" {
|
||||
printStageHelp(stage)
|
||||
var err error
|
||||
cmdString, err = reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
stage = inputs.assignToNext(stage, strings.TrimSpace(cmdString), cfg)
|
||||
|
||||
@ -226,7 +261,7 @@ func registerInteractive(ctx context.Context, configFile string) error {
|
||||
}
|
||||
|
||||
if stage <= StageUnknown {
|
||||
log.Errorf("无效输入,请重新运行 act 命令。")
|
||||
log.Errorf("无效输入,请重新运行命令。")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -255,18 +290,7 @@ func registerNoInteractive(ctx context.Context, configFile string, regArgs *regi
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inputs := ®isterInputs{
|
||||
InstanceAddr: regArgs.InstanceAddr,
|
||||
Token: regArgs.Token,
|
||||
RunnerName: regArgs.RunnerName,
|
||||
Labels: defaultLabels,
|
||||
Ephemeral: regArgs.Ephemeral,
|
||||
}
|
||||
regArgs.Labels = strings.TrimSpace(regArgs.Labels)
|
||||
// 命令行标志。
|
||||
if regArgs.Labels != "" {
|
||||
inputs.Labels = strings.Split(regArgs.Labels, ",")
|
||||
}
|
||||
inputs := initInputs(regArgs)
|
||||
// 配置文件中指定的标签。
|
||||
if len(cfg.Runner.Labels) > 0 {
|
||||
if regArgs.Labels != "" {
|
||||
@ -274,14 +298,17 @@ func registerNoInteractive(ctx context.Context, configFile string, regArgs *regi
|
||||
}
|
||||
inputs.Labels = cfg.Runner.Labels
|
||||
}
|
||||
if len(inputs.Labels) == 0 {
|
||||
inputs.Labels = defaultLabels
|
||||
}
|
||||
|
||||
if inputs.RunnerName == "" {
|
||||
inputs.RunnerName, _ = os.Hostname()
|
||||
log.Infof("运行器名称为空,使用主机名 '%s'。", inputs.RunnerName)
|
||||
}
|
||||
if err := inputs.validate(); err != nil {
|
||||
log.WithError(err).Errorf("无效输入,请重新运行 act 命令。")
|
||||
return nil
|
||||
log.WithError(err).Errorf("无效输入,请重新运行命令。")
|
||||
return err
|
||||
}
|
||||
if err := doRegister(ctx, cfg, inputs); err != nil {
|
||||
return fmt.Errorf("注册运行器失败: %w", err)
|
||||
|
19
internal/app/cmd/register_test.go
Normal file
19
internal/app/cmd/register_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestRegisterNonInteractiveReturnsLabelValidationError(t *testing.T) {
|
||||
err := registerNoInteractive(t.Context(), "", ®isterArgs{
|
||||
Labels: "标签:无效",
|
||||
Token: "token",
|
||||
InstanceAddr: "http://localhost:3000",
|
||||
})
|
||||
assert.Error(t, err, "不支持的标签: 无效")
|
||||
}
|
@ -30,16 +30,16 @@ import (
|
||||
|
||||
// Runner 运行流水线
|
||||
type Runner struct {
|
||||
name string // Runner 名称
|
||||
name string // Runner 名称
|
||||
|
||||
cfg *config.Config // 配置信息
|
||||
|
||||
client client.Client // Gitea 客户端
|
||||
labels labels.Labels // 标签集合
|
||||
envs map[string]string // 环境变量
|
||||
|
||||
// 正在运行的任务
|
||||
runningTasks sync.Map // 使用 sync.Map 来存储正在运行的任务 ID
|
||||
cfg *config.Config // 配置信息
|
||||
|
||||
client client.Client // Gitea 客户端
|
||||
labels labels.Labels // 标签集合
|
||||
envs map[string]string // 环境变量
|
||||
|
||||
// 正在运行的任务
|
||||
runningTasks sync.Map // 使用 sync.Map 来存储正在运行的任务 ID
|
||||
}
|
||||
|
||||
// NewRunner 使用提供的配置、注册信息和客户端创建一个新的 Runner 实例
|
||||
@ -173,6 +173,12 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
|
||||
preset.Token = t
|
||||
}
|
||||
|
||||
if actionsIdTokenRequestUrl := taskContext["actions_id_token_request_url"].GetStringValue(); actionsIdTokenRequestUrl != "" {
|
||||
r.envs["ACTIONS_ID_TOKEN_REQUEST_URL"] = actionsIdTokenRequestUrl
|
||||
r.envs["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = taskContext["actions_id_token_request_token"].GetStringValue()
|
||||
task.Secrets["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = r.envs["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]
|
||||
}
|
||||
|
||||
giteaRuntimeToken := taskContext["gitea_runtime_token"].GetStringValue()
|
||||
if giteaRuntimeToken == "" {
|
||||
// 兼容旧版本 Gitea Server
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func Test_generateWorkflow(t *testing.T) {
|
||||
func Test_生成工作流(t *testing.T) {
|
||||
type args struct {
|
||||
task *runnerv1.Task
|
||||
}
|
||||
@ -24,32 +24,32 @@ func Test_generateWorkflow(t *testing.T) {
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "has needs",
|
||||
name: "有需求",
|
||||
args: args{
|
||||
task: &runnerv1.Task{
|
||||
WorkflowPayload: []byte(`
|
||||
name: Build and deploy
|
||||
name: 构建部署测试
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
job9:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: linux-loong64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- run: ./deploy --build ${{ needs.job1.outputs.output1 }}
|
||||
- run: ./deploy --build ${{ needs.job2.outputs.output2 }}
|
||||
`),
|
||||
Needs: map[string]*runnerv1.TaskNeed{
|
||||
"job1": {
|
||||
Outputs: map[string]string{
|
||||
"output1": "output1 value",
|
||||
"output1": "输出1值",
|
||||
},
|
||||
Result: runnerv1.Result_RESULT_SUCCESS,
|
||||
},
|
||||
"job2": {
|
||||
Outputs: map[string]string{
|
||||
"output2": "output2 value",
|
||||
"output2": "输出2值",
|
||||
},
|
||||
Result: runnerv1.Result_RESULT_SUCCESS,
|
||||
},
|
||||
|
@ -6,6 +6,6 @@ package client
|
||||
const (
|
||||
UUIDHeader = "x-runner-uuid"
|
||||
TokenHeader = "x-runner-token"
|
||||
// Deprecated: could be removed after Gitea 1.20 released
|
||||
// 已弃用: 可以在Gitea 1.20发布后删除
|
||||
VersionHeader = "x-runner-version"
|
||||
)
|
||||
|
@ -27,7 +27,7 @@ func getHTTPClient(endpoint string, insecure bool) *http.Client {
|
||||
return http.DefaultClient
|
||||
}
|
||||
|
||||
// New returns a new runner client.
|
||||
// New返回一个新的runner客户端。
|
||||
func New(endpoint string, insecure bool, uuid, token, version string, opts ...connect.ClientOption) *HTTPClient {
|
||||
baseURL := strings.TrimRight(endpoint, "/") + "/api/actions"
|
||||
|
||||
@ -39,7 +39,7 @@ func New(endpoint string, insecure bool, uuid, token, version string, opts ...co
|
||||
if token != "" {
|
||||
req.Header().Set(TokenHeader, token)
|
||||
}
|
||||
// TODO: version will be removed from request header after Gitea 1.20 released.
|
||||
// TODO:version将在Gitea 1.20发布后从请求标头中删除。
|
||||
if version != "" {
|
||||
req.Header().Set(VersionHeader, version)
|
||||
}
|
||||
@ -73,7 +73,7 @@ func (c *HTTPClient) Insecure() bool {
|
||||
|
||||
var _ Client = (*HTTPClient)(nil)
|
||||
|
||||
// An HTTPClient manages communication with the runner API.
|
||||
// HTTPClient管理与runner API的通信。
|
||||
type HTTPClient struct {
|
||||
pingv1connect.PingServiceClient
|
||||
runnerv1connect.RunnerServiceClient
|
||||
|
@ -36,7 +36,7 @@ func Parse(str string) (*Label, error) {
|
||||
label.Arg = splits[2]
|
||||
}
|
||||
if label.Schema != SchemeHost && label.Schema != SchemeDocker {
|
||||
return nil, fmt.Errorf("不支持的 schema: %s", label.Schema)
|
||||
return nil, fmt.Errorf("不支持的标签: %s", label.Schema)
|
||||
}
|
||||
return label, nil
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
func Test解析(t *testing.T) {
|
||||
tests := []struct {
|
||||
args string
|
||||
want *Label
|
||||
|
@ -23,23 +23,23 @@ import (
|
||||
)
|
||||
|
||||
type Reporter struct {
|
||||
ctx context.Context // 上下文,用于控制生命周期
|
||||
cancel context.CancelFunc // 取消函数
|
||||
closed bool // 是否已关闭
|
||||
client client.Client // Gitea 客户端
|
||||
clientM sync.Mutex // 客户端访问互斥锁
|
||||
ctx context.Context // 上下文,用于控制生命周期
|
||||
cancel context.CancelFunc // 取消函数
|
||||
closed bool // 是否已关闭
|
||||
client client.Client // Gitea 客户端
|
||||
clientM sync.Mutex // 客户端访问互斥锁
|
||||
|
||||
logOffset int // 日志偏移量
|
||||
logRows []*runnerv1.LogRow // 日志行缓存
|
||||
logReplacer *strings.Replacer // 日志内容替换器
|
||||
oldnew []string // 需要替换的敏感信息
|
||||
logOffset int // 日志偏移量
|
||||
logRows []*runnerv1.LogRow // 日志行缓存
|
||||
logReplacer *strings.Replacer // 日志内容替换器
|
||||
oldnew []string // 需要替换的敏感信息
|
||||
|
||||
state *runnerv1.TaskState // 任务状态
|
||||
stateMu sync.RWMutex // 状态访问读写锁
|
||||
outputs sync.Map // 输出参数存储
|
||||
state *runnerv1.TaskState // 任务状态
|
||||
stateMu sync.RWMutex // 状态访问读写锁
|
||||
outputs sync.Map // 输出参数存储
|
||||
|
||||
debugOutputEnabled bool // 是否启用调试输出
|
||||
stopCommandEndToken string // 停止命令结束标记
|
||||
debugOutputEnabled bool // 是否启用调试输出
|
||||
stopCommandEndToken string // 停止命令结束标记
|
||||
}
|
||||
|
||||
// NewReporter 构造函数 初始化日志脱敏规则、状态等信息
|
||||
@ -110,7 +110,7 @@ func (r *Reporter) Fire(entry *log.Entry) error {
|
||||
}
|
||||
|
||||
stage := entry.Data["stage"]
|
||||
|
||||
|
||||
if stage != "Main" {
|
||||
if v, ok := entry.Data["jobResult"]; ok {
|
||||
if jobResult, ok := r.parseResult(v); ok {
|
||||
|
@ -19,122 +19,133 @@ import (
|
||||
"git.whlug.cn/LAA/loong_runner/internal/pkg/client/mocks"
|
||||
)
|
||||
|
||||
func TestReporter_parseLogRow(t *testing.T) {
|
||||
func Test记录器_解析输出日志(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
debugOutputEnabled bool
|
||||
args []string
|
||||
want []string
|
||||
name string // 测试用例名称
|
||||
debugOutputEnabled bool // 是否启用调试输出
|
||||
args []string // 输入的日志行
|
||||
want []string // 期望的输出结果
|
||||
}{
|
||||
{
|
||||
"No command", false,
|
||||
[]string{"Hello, world!"},
|
||||
[]string{"Hello, world!"},
|
||||
name: "无命令",
|
||||
debugOutputEnabled: false,
|
||||
args: []string{"你好,世界!"},
|
||||
want: []string{"你好,世界!"},
|
||||
},
|
||||
{
|
||||
"Add-mask", false,
|
||||
[]string{
|
||||
"foo mysecret bar",
|
||||
"::add-mask::mysecret",
|
||||
"foo mysecret bar",
|
||||
name: "添加掩码",
|
||||
debugOutputEnabled: false,
|
||||
args: []string{
|
||||
"foo 我的密钥 bar", // 输入日志:普通日志行
|
||||
"::add-mask::我的密钥", // 输入命令:添加掩码
|
||||
"foo 我的密钥 bar", // 输入日志:再次普通日志行
|
||||
},
|
||||
[]string{
|
||||
"foo mysecret bar",
|
||||
"<nil>",
|
||||
"foo *** bar",
|
||||
want: []string{
|
||||
"foo 我的密钥 bar", // 原始日志直接输出
|
||||
"<nil>", // 添加掩码命令处理结果(无输出内容)
|
||||
"foo *** bar", // 掩码替换后的日志行
|
||||
},
|
||||
},
|
||||
{
|
||||
"Debug enabled", true,
|
||||
[]string{
|
||||
"::debug::GitHub Actions runtime token access controls",
|
||||
name: "启用调试",
|
||||
debugOutputEnabled: true,
|
||||
args: []string{
|
||||
"::debug::GitHub Actions 运行时令牌访问控制",
|
||||
},
|
||||
[]string{
|
||||
"GitHub Actions runtime token access controls",
|
||||
want: []string{
|
||||
"GitHub Actions 运行时令牌访问控制", // 调试信息直接输出
|
||||
},
|
||||
},
|
||||
{
|
||||
"Debug not enabled", false,
|
||||
[]string{
|
||||
"::debug::GitHub Actions runtime token access controls",
|
||||
name: "禁用调试",
|
||||
debugOutputEnabled: false,
|
||||
args: []string{
|
||||
"::debug::GitHub Actions 运行时令牌访问控制",
|
||||
},
|
||||
[]string{
|
||||
"<nil>",
|
||||
want: []string{
|
||||
"<nil>", // 调试信息被忽略
|
||||
},
|
||||
},
|
||||
{
|
||||
"notice", false,
|
||||
[]string{
|
||||
"::notice file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
|
||||
name: "通知",
|
||||
debugOutputEnabled: false,
|
||||
args: []string{
|
||||
"::notice file=文件.name,line=42,endLine=48,title=酷标题::天啊,这行不通",
|
||||
},
|
||||
[]string{
|
||||
"::notice file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
|
||||
want: []string{
|
||||
"::notice file=文件.name,line=42,endLine=48,title=酷标题::天啊,这行不通", // 通知日志原样输出
|
||||
},
|
||||
},
|
||||
{
|
||||
"warning", false,
|
||||
[]string{
|
||||
"::warning file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
|
||||
name: "警告",
|
||||
debugOutputEnabled: false,
|
||||
args: []string{
|
||||
"::warning file=文件.name,line=42,endLine=48,title=酷标题::天啊,这行不通",
|
||||
},
|
||||
[]string{
|
||||
"::warning file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
|
||||
want: []string{
|
||||
"::warning file=文件.name,line=42,endLine=48,title=酷标题::天啊,这行不通", // 警告日志原样输出
|
||||
},
|
||||
},
|
||||
{
|
||||
"error", false,
|
||||
[]string{
|
||||
"::error file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
|
||||
name: "错误",
|
||||
debugOutputEnabled: false,
|
||||
args: []string{
|
||||
"::error file=文件.name,line=42,endLine=48,title=酷标题::天啊,这行不通",
|
||||
},
|
||||
[]string{
|
||||
"::error file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
|
||||
want: []string{
|
||||
"::error file=文件.name,line=42,endLine=48,title=酷标题::天啊,这行不通", // 错误日志原样输出
|
||||
},
|
||||
},
|
||||
{
|
||||
"group", false,
|
||||
[]string{
|
||||
"::group::",
|
||||
"::endgroup::",
|
||||
name: "分组",
|
||||
debugOutputEnabled: false,
|
||||
args: []string{
|
||||
"::group::", // 开始分组
|
||||
"::endgroup::", // 结束分组
|
||||
},
|
||||
[]string{
|
||||
"::group::",
|
||||
"::endgroup::",
|
||||
want: []string{
|
||||
"::group::", // 分组开始标记原样输出
|
||||
"::endgroup::", // 分组结束标记原样输出
|
||||
},
|
||||
},
|
||||
{
|
||||
"stop-commands", false,
|
||||
[]string{
|
||||
"::add-mask::foo",
|
||||
"::stop-commands::myverycoolstoptoken",
|
||||
"::add-mask::bar",
|
||||
"::debug::Stuff",
|
||||
"myverycoolstoptoken",
|
||||
"::add-mask::baz",
|
||||
"::myverycoolstoptoken::",
|
||||
"::add-mask::wibble",
|
||||
"foo bar baz wibble",
|
||||
name: "停止命令",
|
||||
debugOutputEnabled: false,
|
||||
args: []string{
|
||||
"::add-mask::foo", // 添加掩码命令
|
||||
"::stop-commands::我的停止令牌", // 停止命令标记
|
||||
"::add-mask::bar", // 被忽略的添加掩码命令
|
||||
"::debug::调试信息", // 被忽略的调试信息
|
||||
"我的停止令牌", // 停止命令标记结束
|
||||
"::add-mask::baz", // 恢复处理的添加掩码命令
|
||||
"::我的停止令牌::", // 另一种停止命令标记
|
||||
"::add-mask::wibble", // 被忽略的添加掩码命令
|
||||
"foo bar baz wibble", // 普通日志行
|
||||
},
|
||||
[]string{
|
||||
"<nil>",
|
||||
"<nil>",
|
||||
"::add-mask::bar",
|
||||
"::debug::Stuff",
|
||||
"myverycoolstoptoken",
|
||||
"::add-mask::baz",
|
||||
"<nil>",
|
||||
"<nil>",
|
||||
"*** bar baz ***",
|
||||
want: []string{
|
||||
"<nil>", // 第一个添加掩码命令处理结果
|
||||
"<nil>", // 停止命令标记处理结果
|
||||
"::add-mask::bar", // 被忽略的命令原样输出
|
||||
"::debug::调试信息", // 被忽略的调试信息原样输出
|
||||
"我的停止令牌", // 停止标记结束原样输出
|
||||
"::add-mask::baz", // 恢复处理的添加掩码命令
|
||||
"<nil>", // 无效停止命令标记处理结果
|
||||
"<nil>", // 被忽略的添加掩码命令处理结果
|
||||
"*** bar baz ***", // 掩码替换后的日志行
|
||||
},
|
||||
},
|
||||
{
|
||||
"unknown command", false,
|
||||
[]string{
|
||||
"::set-mask::foo",
|
||||
name: "未知命令",
|
||||
debugOutputEnabled: false,
|
||||
args: []string{
|
||||
"::set-mask::foo", // 未知命令
|
||||
},
|
||||
[]string{
|
||||
"::set-mask::foo",
|
||||
want: []string{
|
||||
"::set-mask::foo", // 未知命令原样输出
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Reporter{
|
||||
@ -155,43 +166,58 @@ func TestReporter_parseLogRow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReporter_Fire(t *testing.T) {
|
||||
t.Run("ignore command lines", func(t *testing.T) {
|
||||
// 测试 Reporter 的 Fire 方法(验证命令行忽略逻辑)
|
||||
func Test记录器_触发(t *testing.T) {
|
||||
t.Run("忽略命令行", func(t *testing.T) { // 测试场景:验证是否正确处理需要忽略的命令行日志
|
||||
// 创建模拟客户端
|
||||
client := mocks.NewClient(t)
|
||||
|
||||
// 模拟 UpdateLog 接口调用,记录请求内容并返回响应
|
||||
client.On("UpdateLog", mock.Anything, mock.Anything).Return(func(_ context.Context, req *connect_go.Request[runnerv1.UpdateLogRequest]) (*connect_go.Response[runnerv1.UpdateLogResponse], error) {
|
||||
t.Logf("Received UpdateLog: %s", req.Msg.String())
|
||||
t.Logf("收到 UpdateLog 请求:%s", req.Msg.String()) // 记录日志请求内容
|
||||
return connect_go.NewResponse(&runnerv1.UpdateLogResponse{
|
||||
AckIndex: req.Msg.Index + int64(len(req.Msg.Rows)),
|
||||
AckIndex: req.Msg.Index + int64(len(req.Msg.Rows)), // 计算确认索引
|
||||
}), nil
|
||||
})
|
||||
|
||||
// 模拟 UpdateTask 接口调用
|
||||
client.On("UpdateTask", mock.Anything, mock.Anything).Return(func(_ context.Context, req *connect_go.Request[runnerv1.UpdateTaskRequest]) (*connect_go.Response[runnerv1.UpdateTaskResponse], error) {
|
||||
t.Logf("Received UpdateTask: %s", req.Msg.String())
|
||||
t.Logf("收到 UpdateTask 请求:%s", req.Msg.String()) // 记录任务更新请求
|
||||
return connect_go.NewResponse(&runnerv1.UpdateTaskResponse{}), nil
|
||||
})
|
||||
|
||||
// 初始化上下文和任务
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
taskCtx, err := structpb.NewStruct(map[string]interface{}{})
|
||||
taskCtx, err := structpb.NewStruct(map[string]interface{}{}) // 创建空任务上下文
|
||||
require.NoError(t, err)
|
||||
|
||||
// 创建 Reporter 实例
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{
|
||||
Context: taskCtx,
|
||||
Context: taskCtx, // 注入任务上下文
|
||||
})
|
||||
defer func() {
|
||||
assert.NoError(t, reporter.Close(""))
|
||||
assert.NoError(t, reporter.Close("")) // 测试结束关闭 Reporter
|
||||
}()
|
||||
reporter.ResetSteps(5)
|
||||
|
||||
reporter.ResetSteps(5) // 初始化5个步骤的日志存储
|
||||
|
||||
// 定义步骤0的日志元数据
|
||||
dataStep0 := map[string]interface{}{
|
||||
"stage": "Main",
|
||||
"stepNumber": 0,
|
||||
"raw_output": true,
|
||||
"stage": "Main", // 阶段名称
|
||||
"stepNumber": 0, // 步骤编号
|
||||
"raw_output": true, // 启用原始输出模式
|
||||
}
|
||||
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "regular log line", Data: dataStep0}))
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "::debug::debug log line", Data: dataStep0}))
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "regular log line", Data: dataStep0}))
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "::debug::debug log line", Data: dataStep0}))
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "::debug::debug log line", Data: dataStep0}))
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "regular log line", Data: dataStep0}))
|
||||
// 发送混合类型的日志条目 ---------------------------------------------------
|
||||
// 预期:普通日志被记录,调试日志被忽略
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "普通日志行", Data: dataStep0}))
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "::debug::调试日志行", Data: dataStep0})) // 应被忽略
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "普通日志行", Data: dataStep0}))
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "::debug::调试日志行", Data: dataStep0})) // 应被忽略
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "::debug::调试日志行", Data: dataStep0})) // 应被忽略
|
||||
assert.NoError(t, reporter.Fire(&log.Entry{Message: "普通日志行", Data: dataStep0}))
|
||||
|
||||
assert.Equal(t, int64(3), reporter.state.Steps[0].LogLength)
|
||||
// 验证结果:步骤0应只有3条普通日志(调试日志被过滤)
|
||||
assert.Equal(t, int64(3), reporter.state.Steps[0].LogLength, "普通日志数量不符预期")
|
||||
})
|
||||
}
|
||||
|
2
main.go
2
main.go
@ -14,6 +14,6 @@ import (
|
||||
func main() {
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
// run the command
|
||||
// 运行命令
|
||||
cmd.Execute(ctx)
|
||||
}
|
||||
|
@ -20,6 +20,13 @@ EXTRA_ARGS=""
|
||||
if [[ ! -z "${GITEA_RUNNER_LABELS}" ]]; then
|
||||
EXTRA_ARGS="${EXTRA_ARGS} --labels ${GITEA_RUNNER_LABELS}"
|
||||
fi
|
||||
if [[ ! -z "${GITEA_RUNNER_EPHEMERAL}" ]]; then
|
||||
EXTRA_ARGS="${EXTRA_ARGS} --ephemeral"
|
||||
fi
|
||||
RUN_ARGS=""
|
||||
if [[ ! -z "${GITEA_RUNNER_ONCE}" ]]; then
|
||||
RUN_ARGS="${RUN_ARGS} --once"
|
||||
fi
|
||||
|
||||
# 如果没有设置令牌,可以从文件中读取令牌,例如从 Docker Secret
|
||||
if [[ -z "${GITEA_RUNNER_REGISTRATION_TOKEN}" ]] && [[ -f "${GITEA_RUNNER_REGISTRATION_TOKEN_FILE}" ]]; then
|
||||
@ -60,4 +67,4 @@ unset GITEA_RUNNER_REGISTRATION_TOKEN
|
||||
unset GITEA_RUNNER_REGISTRATION_TOKEN_FILE
|
||||
|
||||
# 启动 act_runner 守护进程
|
||||
exec act_runner daemon ${CONFIG_ARG}
|
||||
exec act_runner daemon ${CONFIG_ARG} ${RUN_ARGS}
|
||||
|
Reference in New Issue
Block a user