跳至主要内容

创建 Go Actions

·阅读时长 7 分钟
Zetta
Gitea 维护者
techknowlogick
Gitea 维护者
pat-s
Gitea 维护者

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.ymlaction.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 选项卡中查看结果。

use-go-action-main

输入和输出

您可以通过输入参数指定 action 将使用的数据,以及 action 可以通过输出参数提供给其他 actions 的数据。有关更多信息,请参阅 GitHub 关于 输入输出 的文档

要使用 inputsoutputs,您需要更新 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 的工作流中,您需要添加与 inputsoutputs 相关的代码

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 }}'

推送更新后的工作流文件后,您将看到结果

use-go-action-input-and-output

很好!您已成功在 action 中使用 inputsoutputs。您可能会发现用于读取 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"))
}

如果您重新运行工作流,结果将是

use-sdk

如截图所示,需要下载第三方包来构建可执行文件。如果您不想每次都下载第三方包,可以使用 go mod vendor 创建 vendor 目录来存储包。

前置和后置

action.yml 文件中,我们将 runs.main 设置为 main.go,以指定 action 将运行的代码。除了 main 之外,您还可以为 action 指定 prepostpre 允许您在运行 main 之前运行一些代码,post 允许您在作业结束时运行一些代码。如果您需要更多信息,请阅读 GitHub 关于 prepost 的文档。

让我们将 prepost 添加到 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 的输出

go-action-pre

以及 Complete job 中的 post 的输出

go-action-post


现在您应该对 Gitea 的 Go actions 有了基本的了解。如果您有任何想法或遇到任何错误,请随时创建问题或拉取请求。我们可以共同努力让 Gitea Actions 变得更好。


您可以在以下仓库中找到演示代码

另外,还有一个可用的 Go action 可以将发布内容发布到 Gitea https://gitea.com/actions/release-action

An icon showing wave propagation

加入我们的社区

Gitea 是开源的。为我们的 GitHub 仓库加星,并加入我们在 Discord 上的社区!

An icon showing a paper plane

订阅我们的新闻简报

关注 Gitea 的最新动态