创建 Go Actions
Gitea 的 CI 系统 Gitea Actions 最近在 1.19.0 版本中发布。Gitea Actions 是一个内置的 CI/CD 系统,与 GitHub Actions 几乎兼容。一个关键区别是,Gitea 同时支持 Go action 和 JavaScript action,而 GitHub 仅原生支持 JavaScript action(不过你可以使用任何编程语言来编写 Docker 容器 action)。
Go action 为不熟悉 JavaScript 的 Go 开发者提供了一种创建原生 action 的方法。 然而,与 JavaScript action 相比,Go action 可能更慢,因为它们在运行前需要被构建为可执行文件。但你无需在 Go action 中将生成的 JavaScript 文件放入 dist。
在本指南中,我们将展示如何在 Gitea 中创建 Go action。在开始之前,你应该对 Gitea Actions 有基本的了解。如果你还不熟悉,建议阅读 深入 Gitea Actions。
一个简单的 Go Action
首先,让我们创建一个简单的 Go action。你需要在你的 Gitea 实例上创建一个名为 simple-go-action 的仓库并克隆它:
git clone <repo-url>
cd simple-go-action
# create the go.mod file
go mod init simple-go-action
元数据文件对于 action 至关重要,其文件名必须是 action.yml 或 action.yaml。下面是一个 action.yml 文件示例:
name: 'Simple Go Action'
description: 'A simple Gitea action written in go'
runs:
using: 'go'
main: 'main.go'
要了解更多元数据语法,可以查看 GitHub Actions 的元数据语法。
你可能会注意到,我们在元数据中使用了 using: 'go',指定 Go 作为此 action 的运行时。由于 GitHub 不原生支持 Go action,你不会在 GitHub 的文档中找到这一点。
接下来,添加 main.go 文件,其中包含打印 "Hello world" 字符串的简单逻辑:
package main
import "fmt"
func main() {
fmt.Println("Hello world")
}
现在 action 已准备好进行测试。提交代码并推送到 Gitea。要测试该 action,请创建另一个名为 test-simple-go-action 的仓库,并使用以下代码添加一个工作流文件。
请将 <action-url> 替换为你的 action 的 URL。<action-url> 应该类似于 http(s)://<your-gitea-instance-url>/<owner>/<repo>@<version>,例如:https://gitea.com/Zettat123/simple-go-action@v1。<version> 可以是标签、分支或提交 SHA。更多信息请参见 使用发布管理 action。
name: 'Test Go Action'
on: [push]
jobs:
use-go-action:
runs-on: ubuntu-latest
steps:
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
- name: Use Go Action
uses: <action-url>
由于该 action 是用 Go 编写的,而 runs-on 环境可能没有安装 Go,你需要在使用该 action 之前设置 Go 运行时。setup-go action 在这种情况下效果很好,但我们稍后会讨论不适用的情况。推送工作流文件后,你可以在 Actions 标签页中查看结果。

输入与输出
你可以通过 inputs 参数指定操作将使用的数据,并通过 outputs 参数指定操作可以提供给其他操作的数据。更多信息,请参阅 GitHub 文档中的 inputs 和 outputs。
要使用 inputs 和 outputs,你需要更新 action.yml 文件:
name: 'Simple Go Action'
description: 'A simple Gitea action written in go'
inputs:
username:
description: 'The username to print'
required: true
outputs:
time:
description: 'The time when the action was called'
runs:
using: 'go'
main: 'main.go'
你还需要更新 main.go 文件:
package main
import (
"fmt"
"os"
"time"
)
func main() {
username := readInputs()
fmt.Printf("username is %s\n", username)
err := writeOutputs("time", time.Now().Format("2006-01-02 15:04:05"))
if err != nil {
panic(err)
}
}
func readInputs() string {
username := os.Getenv("INPUT_USERNAME")
return username
}
func writeOutputs(k, v string) (err error) {
msg := fmt.Sprintf("%s=%s", k, v)
outputFilepath := os.Getenv("GITHUB_OUTPUT")
f, err := os.OpenFile(outputFilepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return
}
defer func() {
if cErr := f.Close(); cErr != nil && err == nil {
err = cErr
}
}()
if _, err = f.Write([]byte(msg)); err != nil {
return
}
return
}
在使用该操作的工作流中,你需要添加与 inputs 和 outputs 相关的代码:
name: 'Test Go Action'
on: [push]
jobs:
use-go-action:
runs-on: ubuntu-latest
steps:
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
- name: Use Go Action
id: use-go-action
uses: <action-url>
with:
username: foo
- name: Print Output
run: echo 'output time is ${{ steps.use-go-action.outputs.time }}'
推送更新后的工作流文件后,你将看到结果:

很好!你已成功在你的操作中使用了 inputs 和 outputs。
你可能会发现,用于读取 inputs 和写入 outputs 的代码看起来有些复杂。为了简化代码,你可以使用 go-githubactions SDK:
package main
import (
"fmt"
"time"
gha "github.com/sethvargo/go-githubactions"
)
func main() {
username := gha.GetInput("username")
fmt.Printf("username is %s\n", username)
gha.SetOutput("time", time.Now().Format("2006-01-02 15:04:05"))
}
如果重新运行工作流,结果将如下:

如截图所示,构建可执行文件需要下载第三方软件包。如果你不想每次都下载第三方软件包,可以使用 go mod vendor 创建 vendor 目录来存储这些软件包。
前置与后置
在 action.yml 文件中,我们将 runs.main 设置为 main.go,以指定该操作将要运行的代码。
除了 main 之外,你还可以为操作指定 pre 和 post。
pre 允许你在运行 main 之前执行一些代码,而 post 允许你在作业结束时执行一些代码。
如果你需要更多信息,请阅读 GitHub 关于 pre 和 post 的文档。
让我们将 pre 和 post 添加到该操作中。
首先,你需要创建一个 pre 目录,并在其中添加 pre.go 文件:
package main
import "fmt"
func main() {
fmt.Println("Pre of Simple Go Action")
}
接下来,以类似的方式创建 post 目录和 post.go 文件:
package main
import "fmt"
func main() {
fmt.Println("Post of Simple Go Action")
}
你还需要更新 action.yml 文件:
name: 'Simple Go Action'
description: 'A simple Gitea action written in go'
inputs:
username:
description: 'The username to print'
required: true
outputs:
time:
description: 'The time when the action was called'
runs:
using: 'go'
main: 'main.go'
pre: "pre/pre.go"
post: "post/post.go"
现在目录结构应该如下:
├── action.yml
├── go.mod
├── main.go
├── post
│ └── post.go
└── pre
└── pre.go
一切看起来没问题!但当你重新运行工作流时,可能会看到一个错误:"go": executable file not found in $PATH。
原因是 pre 中的代码会在调用 setup-go 操作之前执行,而 Go 运行时尚未安装。
在这种情况下,你需要使用已安装 Go 的镜像来运行此工作流,并且由于镜像已包含 Go 运行时,因此无需调用 setup-go 操作。
你可以使用 container.image 指定镜像(将 <image-with-go> 替换为已安装 Go 的镜像):
name: 'Test Go Action'
on: [push]
jobs:
use-go-action:
runs-on: ubuntu-latest
container:
image: <image-with-go>
steps:
- name: Use Go Action
id: use-go-action
uses: <action-url>
with:
username: foo
- name: Print Output
run: echo 'output time is ${{ steps.use-go-action.outputs.time }}'
这样工作流就会正常运行。你将在 Set up job 中看到 pre 的输出:
![]()
并在 Complete job 中看到 post 的输出:
![]()
现在你应该对 Gitea 的 Go 操作有了基本了解。 如果你有任何想法或遇到任何错误,请随时创建工单或合并请求。 我们可以一起努力让 Gitea Actions 变得更好。
你可以在以下仓库中找到演示代码:
- simple-go-action:https://gitea.com/Zettat123/simple-go-action
- test-simple-go-action:https://gitea.com/Zettat123/test-simple-go-action
此外,还有一个可用的 Go 操作,用于向 Gitea 发布版本 https://gitea.com/actions/release-action


