golang创建构建管道的自动化工具插件goyek的使用

golang创建构建管道的自动化工具插件goyek的使用

goyek是一个任务自动化库,旨在替代Make、Mage和Task等工具。它具有以下主要特性:

  • 作为库而非应用程序,API设计灵感来自testing、cobra、flag和http包
  • 跨平台且不依赖特定shell
  • 无需二进制安装
  • 调试简单,就像普通Go代码
  • 任务定义方式类似cobra命令
  • 任务动作类似于Go测试
  • 可以重用任何Go代码和库
  • 高度可定制
  • 无第三方依赖

使用示例

创建构建自动化代码

build目录下创建以下文件:

build/hello.go:

package main

import (
	"flag"

	"github.com/goyek/goyek/v2"
	"github.com/goyek/x/cmd"
)

// 定义命令行参数
var msg = flag.String("msg", "greeting message", "Hello world!")

// 定义hello任务
var hello = goyek.Define(goyek.Task{
	Name:  "hello", // 任务名称
	Usage: "demonstration", // 任务说明
	Action: func(a *goyek.A) { // 任务动作
		a.Log(*msg) // 记录日志
		cmd.Exec(a, "go version") // 执行命令
	},
})

build/main.go:

package main

import (
	"os"

	"github.com/goyek/goyek/v2"
	"github.com/goyek/x/boot"
)

func main() {
	// 切换到项目根目录
	if err := os.Chdir(".."); err != nil {
		panic(err)
	}
	// 设置默认任务
	goyek.SetDefault(hello)
	// 启动goyek
	boot.Main()
}

运行示例

  1. 初始化模块并运行帮助:
cd build
go mod tidy
go run . -h

预期输出:

Usage of build: [flags] [--] [tasks]
Tasks:
  hello  demonstration
Flags:
  -dry-run
        print all tasks without executing actions
  -long-run duration
        print when a task takes longer (default 1m0s)
  -msg string
        Hello world! (default "greeting message")
  -no-color
        disable colorizing output
  -no-deps
        do not process dependencies
  -skip comma-separated tasks
        skip processing the comma-separated tasks
  -v    print all tasks as they are run
  1. 运行任务并显示详细输出:
go run . -v

预期输出:

===== TASK  hello
      hello.go:16: greeting message
      hello.go:17: Exec: go version
go version go1.24.0 linux/amd64
----- PASS: hello (0.12s)
ok      0.123s

使用包装脚本

可以使用以下包装脚本替代go run .命令:

  • Bash: goyek.sh
  • PowerShell: goyek.ps1

创建新项目

对于新项目,可以使用goyek/template模板。对于现有项目,只需复制相关文件即可。


更多关于golang创建构建管道的自动化工具插件goyek的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang创建构建管道的自动化工具插件goyek的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用GoYek构建Golang自动化构建管道

GoYek是一个轻量级的Golang构建工具,它允许你创建类似Makefile的构建管道,但完全用Go编写。下面我将详细介绍如何使用GoYek来创建自动化构建管道。

GoYek核心概念

GoYek的核心概念包括:

  • Task:构建任务,可以定义依赖关系
  • Flow:任务执行流程
  • Action:任务实际执行的函数

安装GoYek

首先安装GoYek:

go get github.com/goyek/goyek/v2

基本示例

创建一个简单的构建管道:

package main

import (
	"github.com/goyek/goyek/v2"
	"os/exec"
)

func main() {
	flow := goyek.NewFlow()

	// 定义clean任务
	clean := flow.Register(goyek.Task{
		Name:    "clean",
		Usage:   "清理构建产物",
		Command: cleanCmd,
	})

	// 定义build任务,依赖clean
	build := flow.Register(goyek.Task{
		Name:    "build",
		Usage:   "构建项目",
		Command: buildCmd,
		Deps:    []goyek.Task{clean},
	})

	// 定义test任务,依赖build
	flow.Register(goyek.Task{
		Name:    "test",
		Usage:   "运行测试",
		Command: testCmd,
		Deps:    []goyek.Task{build},
	})

	flow.Main()
}

func cleanCmd(tf *goyek.TF) {
	tf.Log("清理构建目录...")
	cmd := exec.Command("rm", "-rf", "bin/")
	if err := cmd.Run(); err != nil {
		tf.Errorf("清理失败: %v", err)
	}
}

func buildCmd(tf *goyek.TF) {
	tf.Log("构建项目...")
	cmd := exec.Command("go", "build", "-o", "bin/myapp", "./cmd/myapp")
	if err := cmd.Run(); err != nil {
		tf.Errorf("构建失败: %v", err)
	}
}

func testCmd(tf *goyek.TF) {
	tf.Log("运行测试...")
	cmd := exec.Command("go", "test", "./...")
	if err := cmd.Run(); err != nil {
		tf.Errorf("测试失败: %v", err)
	}
}

高级用法

1. 参数传递

flow.Register(goyek.Task{
	Name:    "custom",
	Usage:   "自定义任务",
	Command: func(tf *goyek.TF) {
		// 获取参数
		env := tf.Args().GetString("env", "dev", "环境变量")
		tf.Logf("当前环境: %s", env)
		
		// 根据环境执行不同操作
		if env == "prod" {
			// 生产环境特定逻辑
		}
	},
})

2. 条件执行

flow.Register(goyek.Task{
	Name:    "conditional",
	Command: func(tf *goyek.TF) {
		if tf.Failed() {
			tf.Skip("跳过因为前置任务失败")
			return
		}
		
		// 正常执行逻辑
	},
})

3. 并行执行

flow.Register(goyek.Task{
	Name: "parallel",
	Command: func(tf *goyek.TF) {
		// 使用goroutine并行执行
		var wg sync.WaitGroup
		
		wg.Add(1)
		go func() {
			defer wg.Done()
			// 任务1
		}()
		
		wg.Add(1)
		go func() {
			defer wg.Done()
			// 任务2
		}()
		
		wg.Wait()
	},
})

实际项目示例

下面是一个更完整的实际项目构建管道示例:

package main

import (
	"fmt"
	"github.com/goyek/goyek/v2"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	buildDir = "bin"
)

func main() {
	flow := goyek.NewFlow()

	// 基础任务
	setup := flow.Register(goyek.Task{
		Name:    "setup",
		Usage:   "初始化构建环境",
		Command: setupCmd,
	})

	// 代码生成
	generate := flow.Register(goyek.Task{
		Name:    "generate",
		Usage:   "生成代码",
		Command: generateCmd,
		Deps:    []goyek.Task{setup},
	})

	// 代码格式化
	format := flow.Register(goyek.Task{
		Name:    "fmt",
		Usage:   "格式化代码",
		Command: fmtCmd,
		Deps:    []goyek.Task{setup},
	})

	// 构建
	build := flow.Register(goyek.Task{
		Name:    "build",
		Usage:   "构建项目",
		Command: buildCmd,
		Deps:    []goyek.Task{generate, format},
	})

	// 测试
	test := flow.Register(goyek.Task{
		Name:    "test",
		Usage:   "运行测试",
		Command: testCmd,
		Deps:    []goyek.Task{build},
	})

	// 代码检查
	lint := flow.Register(goyek.Task{
		Name:    "lint",
		Usage:   "静态代码分析",
		Command: lintCmd,
		Deps:    []goyek.Task{generate},
	})

	// 默认任务
	flow.Register(goyek.Task{
		Name:    "default",
		Usage:   "默认任务: 格式化、构建、测试",
		Command: func(tf *goyek.TF) { tf.Log("默认任务完成") },
		Deps:    []goyek.Task{fmt, build, test, lint},
	})

	flow.Main()
}

func setupCmd(tf *goyek.TF) {
	// 创建构建目录
	if err := exec.Command("mkdir", "-p", buildDir).Run(); err != nil {
		tf.Errorf("创建构建目录失败: %v", err)
		return
	}
	tf.Log("构建环境初始化完成")
}

func generateCmd(tf *goyek.TF) {
	tf.Log("生成代码...")
	cmd := exec.Command("go", "generate", "./...")
	if err := cmd.Run(); err != nil {
		tf.Errorf("代码生成失败: %v", err)
	}
}

func fmtCmd(tf *goyek.TF) {
	tf.Log("格式化代码...")
	cmd := exec.Command("gofmt", "-s", "-w", ".")
	if err := cmd.Run(); err != nil {
		tf.Errorf("格式化失败: %v", err)
	}
}

func buildCmd(tf *goyek.TF) {
	tf.Log("构建项目...")
	
	// 获取所有main包
	mainPkgs, err := findMainPackages()
	if err != nil {
		tf.Errorf("查找main包失败: %v", err)
		return
	}

	// 为每个main包构建
	for _, pkg := range mainPkgs {
		output := filepath.Join(buildDir, filepath.Base(pkg))
		cmd := exec.Command("go", "build", "-o", output, pkg)
		if err := cmd.Run(); err != nil {
			tf.Errorf("构建 %s 失败: %v", pkg, err)
		} else {
			tf.Logf("成功构建: %s -> %s", pkg, output)
		}
	}
}

func testCmd(tf *goyek.TF) {
	tf.Log("运行测试...")
	cmd := exec.Command("go", "test", "-race", "-cover", "./...")
	if err := cmd.Run(); err != nil {
		tf.Errorf("测试失败: %v", err)
	}
}

func lintCmd(tf *goyek.TF) {
	tf.Log("静态代码分析...")
	cmd := exec.Command("golangci-lint", "run")
	if err := cmd.Run(); err != nil {
		tf.Errorf("代码检查失败: %v", err)
	}
}

// 辅助函数:查找所有包含main包的目录
func findMainPackages() ([]string, error) {
	cmd := exec.Command("go", "list", "-f", "{{.Dir}}", "main")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("执行go list失败: %v", err)
	}
	
	dirs := strings.Split(strings.TrimSpace(string(output)), "\n")
	if len(dirs) == 1 && dirs[0] == "" {
		return nil, nil
	}
	return dirs, nil
}

使用建议

  1. 任务拆分:将大任务拆分为小任务,每个任务只做一件事
  2. 依赖管理:合理设置任务依赖关系
  3. 错误处理:每个任务都应该有良好的错误处理
  4. 日志输出:使用tf.Log和tf.Errorf提供清晰的构建日志
  5. 参数化:使用参数使任务更灵活

GoYek是一个强大而简单的工具,特别适合Golang项目的构建自动化。它结合了Makefile的简洁性和Go语言的强大功能,让你的构建脚本更易于维护和扩展。

回到顶部