创建 Go Actions
Gitea 的 CI 系统,Gitea Actions,最近在 1.19.0 版本中发布。Gitea Actions 是一个内部 CI/CD 系统,提供与 GitHub Actions 几乎相同的兼容性。一个主要区别是 Gitea 支持 Go 和 JavaScript actions,而 GitHub 仅原生支持 JavaScript actions(虽然您可以使用任何编程语言创建 Docker 容器 actions)。
Go actions 为不熟悉 JavaScript 的 Go 开发者提供了一种创建原生 actions 的方式。但是,与 JavaScript actions 相比,Go actions 速度可能会更慢,因为它们需要在运行之前构建为可执行文件。但是,您不需要将生成的 javascript 文件放入 Go action 的 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
元数据文件对于 actions 至关重要,其文件名必须是 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 actions,您不会在 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。有关更多信息,请参阅 使用发布管理进行 actions。
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 选项卡中查看结果。
输入和输出
您可以通过输入参数指定 action 将使用的数据,以及 action 可以通过输出参数提供给其他 actions 的数据。有关更多信息,请参阅 GitHub 关于 输入 和 输出 的文档
要使用 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
}
在使用 action 的工作流中,您需要添加与 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 }}'
推送更新后的工作流文件后,您将看到结果
很好!您已成功在 action 中使用 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
,以指定 action 将运行的代码。除了 main
之外,您还可以为 action 指定 pre
和 post
。pre
允许您在运行 main
之前运行一些代码,post
允许您在作业结束时运行一些代码。如果您需要更多信息,请阅读 GitHub 关于 pre 和 post 的文档。
让我们将 pre
和 post
添加到 action 中。首先,您需要创建一个 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
。原因是在调用 setup-go
action 之前将执行 pre
中的代码,而 Go 运行时尚未设置。在这种情况下,您需要使用安装了 Go 的镜像来运行此工作流,并且您不需要调用 setup-go
action,因为镜像已经拥有 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 actions 有了基本的了解。如果您有任何想法或遇到任何错误,请随时创建问题或拉取请求。我们可以共同努力让 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 action 可以将发布内容发布到 Gitea https://gitea.com/actions/release-action