发布于 2026-01-06 4 阅读
0

通过示例学习 Go:第 8 部分 - 自动交叉编译并发布您的 Go 应用 关于 安装 先决条件 构建应用 运行应用 测试应用 由 Mux 呈现的 DEV 全球展示挑战赛:展示您的项目!

通过示例学习 Go:第 8 部分 - 自动交叉编译并发布您的 Go 应用

关于

安装

先决条件

构建应用程序

运行该应用程序

测试该应用

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

在之前的文章中,我们创建了一个HTTP REST API 服务器、一个CLI、一个Discord 机器人、一个gRPC 应用程序……甚至还有一个任天堂 Game Boy Advance 游戏

正如我们之前看到的,Golang 可以用于多种类型的应用程序,但我个人非常喜欢创建命令行应用程序和工具,热爱 DevOps 理念,也热爱 Go 社区。因此,在本文中,我们将创建一个小工具,遵循一些 Go 最佳实践,自动生成跨平台可执行二进制文件,并通过 GitHub Actions 创建 GitHub 发布版本。

准备好?

初始化

首先,在 GitHub 上创建我们的新存储库(以便共享和开源)。

为此,我登录了GitHub 网站,点击了存储库链接,点击了绿色的“新建”按钮,然后创建了一个名为gophersay的新存储库。

现在,在你的本地计算机上,使用 git clone 命令将这个新仓库克隆到你想要的位置:



$ git clone https://github.com/scraly/gophersay.git
$ cd gophersay


Enter fullscreen mode Exit fullscreen mode

现在,我们需要初始化 Go 模块(依赖管理):



$ go mod init github.com/scraly/gophersay
go: creating new go.mod: module github.com/scraly/gophersay


Enter fullscreen mode Exit fullscreen mode

这将创建一个go.mod类似这样的文件:



module github.com/scraly/gophersay

go 1.16


Enter fullscreen mode Exit fullscreen mode

在开始编写桌面应用程序代码之前,按照良好实践,我们将创建一个简单的代码组织结构。

创建以下文件夹结构:



.
├── bin
├── README.md
└── go.mod


Enter fullscreen mode Exit fullscreen mode

就这些?是的,我们代码组织结构的其余部分很快就会创建完成 ;-)。

让我们来创建我们的应用程序

狐狸地鼠

我们想要什么?

你知道“ cowsay ”这款应用吗?​​它是一款简单的应用,可以把你写的文字显示成牛说的话。

牛叫

我喜欢牛,但我更喜欢地鼠,所以我们想做一个“牛语”版本,用地鼠代替牛。
程序会显示用户输入的文本,并用 ASCII 码显示一个地鼠朗读这段文本。

这个应用看起来可能没什么用,但你会发现,它能让我们看到一些好的做法 ;-)。

首先,你需要将我创建的ASCII 格式的 Gopher 文件提取出来,并放入一个新gophers文件夹中。你需要创建一个类似这样的文件夹:



├── gophers
│   ├── gopher0.txt
│   ├── gopher1.txt
│   ├── gopher2.txt
│   └── gopher3.txt


Enter fullscreen mode Exit fullscreen mode

创建main.go文件。

首先,我们初始化名为 的包main,以及我们需要导入的所有依赖项/库:



package main

import (
    "fmt"
    "log"
    "math/rand"
    "os"
    "strconv"
    "strings"
    "time"

    "embed"
)


Enter fullscreen mode Exit fullscreen mode

然后,我们初始化变量:



// Hey, I want to embed "gophers" folder in the executable binary
// Use embed go 1.16 new feature (for embed gophers static files)
//go:embed gophers
var embedGopherFiles embed.FS


Enter fullscreen mode Exit fullscreen mode

得益于Go 1.16版本中引入的强大的嵌入功能,我们可以告诉变量我们将文件夹嵌入到其中gophers/embedGopherFiles

让我们来创建这个main()函数:



func main() {

    // Display usage/help message
    if len(os.Args) == 1 || (len(os.Args) == 2 && os.Args[1] == "-h") || (len(os.Args) == 2 && os.Args[1] == "--help") {
        usage := "GopherSay is inspired by Cowsay program.\nGopherSay allow you to display a message said by a cute random Gopher.\n\nUsage:\n   gophersay MESSAGE\n\nExample:\n   gophersay hello Gopher lovers"

        fmt.Println(usage)
        return
    } else if len(os.Args) > 1 {

        message := strings.Join(os.Args[1:], " ")
        nbChar := len(message)

        line := " "
        for i := 0; i <= nbChar; i++ {
            line += "-"
        }

        fmt.Println(line)
        fmt.Println("< " + message + " >")
        fmt.Println(line)
        fmt.Println("        \\")
        fmt.Println("         \\")

        // Generate a random integer depending on get the number of ascii files
        rand.Seed(time.Now().UnixNano())
        randInt := rand.Intn(getNbOfGopherFiles() - 1)

        // Display random gopher ASCII embed files
        fileData, err := embedGopherFiles.ReadFile("gophers/gopher" + strconv.Itoa(randInt) + ".txt")
        if err != nil {
            log.Fatal("Error during read gopher ascii file", err)
        }
        fmt.Println(string(fileData))
    }
}


Enter fullscreen mode Exit fullscreen mode

main()现在是时候一步一步地解释这个函数了。

首先,如果用户在不带参数的情况下运行我们的应用程序/工具,或者使用“-h”选项和/或“--help”选项运行,我们会显示用法说明/帮助信息:



    // Display usage/help message
    if len(os.Args) == 1 || (len(os.Args) == 2 && os.Args[1] == "-h") || (len(os.Args) == 2 && os.Args[1] == "--help") {
        usage := "GopherSay is inspired by Cowsay program.\nGopherSay allow you to display a message said by a cute random Gopher.\n\nUsage:\n   gophersay MESSAGE\n\nExample:\n   gophersay hello Gopher lovers"

        fmt.Println(usage)
        return
    }


Enter fullscreen mode Exit fullscreen mode

然后,如果用户gophersay使用参数(文本)运行应用程序,我们会定义一个名为 message 的变量,用于检索所有参数,以及一个用于存储消息字符数的变量。
我们会像“cowsay”程序那样,用“bubble”包围打印出这条消息:



else if len(os.Args) > 1 {

        message := strings.Join(os.Args[1:], " ")
        nbChar := len(message)

        line := " "
        for i := 0; i <= nbChar; i++ {
            line += "-"
        }

        fmt.Println(line)
        fmt.Println("< " + message + " >")
        fmt.Println(line)
        fmt.Println("        \\")
        fmt.Println("         \\")


Enter fullscreen mode Exit fullscreen mode

之后,我们生成一个介于 0 和我们拥有的 gopher 文件数量 - 1 之间的随机整数(目前为 4 - 1,但我计划添加更多):



        // Generate a random integer depending on get the number of ascii files
        rand.Seed(time.Now().UnixNano())
        randInt := rand.Intn(getNbOfGopherFiles() - 1)


Enter fullscreen mode Exit fullscreen mode

等等……为什么要执行rand.Seed()这个函数?

rand.Intn(int)返回一个介于 0 和 n 之间的非负伪随机数。这很棒,但是……它生成的是一个确定性的数值序列!
因此,为了获得“真正的”随机数,解决方案是使用 `nil`rand.Seed()来初始化默认的随机源。

加密货币/兰特

让我们回到代码,接下来我们要显示可爱的 ASCII 格式 Gopher:



        // Display random gopher ASCII embed files
        fileData, err := embedGopherFiles.ReadFile("gophers/gopher" + strconv.Itoa(randInt) + ".txt")
        if err != nil {
            log.Fatal("Error during read gopher ascii file", err)
        }
        fmt.Println(string(fileData))


Enter fullscreen mode Exit fullscreen mode

最后,创建一个函数,返回 ASCII Gopher 图像文件的数量:



func getNbOfGopherFiles() int {

    files, err := embedGopherFiles.ReadDir("gophers")
    if err != nil {
        log.Fatal("Error during reading gophers folder", err)
    }

    nbOfFiles := 0
    for _, _ = range files {
        nbOfFiles++
    }

    return nbOfFiles
}


Enter fullscreen mode Exit fullscreen mode

好的,但是这个著名的嵌入式内容是什么呢?

如果我们只将main.go文件打包成可执行二进制文件,那么当我们执行它时,就会出现问题,因为您的计算机上不存在“gophers/”文件夹。

在 Go 版本 1.16 之前,虽然有一些解决方案,但都不如新embed软件包简单。

embed软件包提供了在编译期间使用新方法访问嵌入在程序中的文件的途径//go:embed directive

//go:embed指令允许在编译时将静态文件和文件夹嵌入到应用程序二进制文件中,而无需使用外部工具。

要使用它,首先我们需要声明一个用于存储嵌入内容的变量。在我们的示例中,我们嵌入的是我们的gophers/文件夹:



//go:embed gophers
var embedGopherFiles embed.FS


Enter fullscreen mode Exit fullscreen mode

然后,我们可以读取该文件夹中的一个文件:



fileData, err := embedGopherFiles.ReadFile("gophers/gopher" + strconv.Itoa(randInt) + ".txt")


Enter fullscreen mode Exit fullscreen mode

获取此文件夹中的文件列表:



files, err := embedGopherFiles.ReadDir("gophers")


Enter fullscreen mode Exit fullscreen mode

您也可以直接嵌入文件:



//go:embed gophers/gopher0.txt
var myFile string


Enter fullscreen mode Exit fullscreen mode

如果嵌入模式指定了一个文件夹,则所有文件都会被嵌入(递归地),但文件名以“.”或“_”开头的文件除外。
如果要嵌入这些文件,需要像这样指定文件夹:myfolder/*

惊人的!

试试看!

代码讲解完毕,现在是时候测试我们的小应用程序了!



$ go run main.go
GopherSay is inspired by Cowsay program.
GopherSay allow you to display a message said by a cute random Gopher.

Usage:
   gophersay MESSAGE

Example:
   gophersay hello Gopher lovers


Enter fullscreen mode Exit fullscreen mode


$ go run main.go --help
GopherSay is inspired by Cowsay program.
GopherSay allow you to display a message said by a cute random Gopher.

Usage:
   gophersay MESSAGE

Example:
   gophersay hello Gopher lovers


Enter fullscreen mode Exit fullscreen mode

太好了,我们有了使用提示信息。



$ go run main.go Hello Gopher lovers!


Enter fullscreen mode Exit fullscreen mode

地鼠

没错!我们的文字是由我们可爱的ASCII地鼠之一朗读的!

建造它!

您的应用程序已准备就绪,您可以开始构建了。

在之前的文章中,我们使用Taskfile来自动化我们的常见任务。

我创建了一个Taskfile.yaml文件:



version: "3"

tasks:
    run: 
        desc: Run the app
        cmds:
        - GOFLAGS=-mod=mod go run main.go

    build:
        desc: Build the app
        cmds:
        - GOFLAGS=-mod=mod go build -o bin/gophersay main.go 

    clean:
        desc: Build the app
        cmds:
        - rm -rf dist 


Enter fullscreen mode Exit fullscreen mode

现在我们可以开始构建我们的应用程序了:



$ task build
task: [build] GOFLAGS=-mod=mod go build -o bin/gophersay main.go


Enter fullscreen mode Exit fullscreen mode

但是……可执行二进制文件仅适用于我们的环境、操作系统和平台,而我想与gophersay全世界分享,所以是时候找到一种轻松交叉编译我们应用程序的方法了!

GoReleaser

GoReleaser

使用GoReleaser,可以做到以下几点:

  • 交叉编译 Go 项目
  • 发布到 GitHub、GitLab 和 Gitea
  • 创建 Docker 镜像和清单。
  • 创建 Linux 软件包和 Homebrew tap
  • ...

哦,拜托,这正是我们想要的!

首先,我们需要安装 GoReleaser

适用于 macOS:



$ brew install goreleaser/tap/goreleaser


Enter fullscreen mode Exit fullscreen mode

运行 init 命令创建.goreleaser.yml配置文件:



$ goreleaser init
   • Generating .goreleaser.yml file
   • config created; please edit accordingly to your needs file=.goreleaser.yml


Enter fullscreen mode Exit fullscreen mode

我们来看一下这个新生成的文件:



# This is an example .goreleaser.yml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
  hooks:
    # You may remove this if you don't use go modules.
    - go mod tidy
    # you may remove this if you don't need go generate
    - go generate ./...
builds:
  - env:
      - CGO_ENABLED=0
    goos:
      - linux
      - windows
      - darwin
archives:
  - replacements:
      darwin: Darwin
      linux: Linux
      windows: Windows
      386: i386
      amd64: x86_64
checksum:
  name_template: 'checksums.txt'
snapshot:
  name_template: "{{ incpatch .Version }}-next"
changelog:
  sort: asc
  filters:
    exclude:
      - '^docs:'
      - '^test:'


Enter fullscreen mode Exit fullscreen mode

这很棒。因为我们go generate的应用程序不需要用到它,所以我们可以删除那一- go generate ./...行代码 ;-)。

让我们运行一次“仅限本地”的发布,在本地生成 Go 应用的版本:



$ goreleaser release --snapshot --skip-publish --rm-dist


Enter fullscreen mode Exit fullscreen mode

/!\ 别忘了使用选项goreleaser release调用此命令--rm-dist,或者您可以执行task clean目标来删除dist/文件夹。否则,您会遇到问题,因为此文件夹必须为空 ^^。

如果我们查看新dist/生成的文件夹,可以看到 GoReleaser 为我们生成了跨平台可执行二进制文件和校验和:



dist
├── checksums.txt
├── config.yaml
├── gophersay_0.0.1-next_Darwin_arm64.tar.gz
├── gophersay_0.0.1-next_Darwin_x86_64.tar.gz
├── gophersay_0.0.1-next_Linux_arm64.tar.gz
├── gophersay_0.0.1-next_Linux_i386.tar.gz
├── gophersay_0.0.1-next_Linux_x86_64.tar.gz
├── gophersay_0.0.1-next_Windows_i386.tar.gz
├── gophersay_0.0.1-next_Windows_x86_64.tar.gz
├── gophersay_darwin_amd64
│   └── gophersay
├── gophersay_darwin_arm64
│   └── gophersay
├── gophersay_linux_386
│   └── gophersay
├── gophersay_linux_amd64
│   └── gophersay
├── gophersay_linux_arm64
│   └── gophersay
├── gophersay_windows_386
│   └── gophersay.exe
└── gophersay_windows_amd64
    └── gophersay.exe


Enter fullscreen mode Exit fullscreen mode

太完美了!

当 GitHub Action 遇上 GoReleaser……

那么,如何自动生成新版本呢?

接下来,我们需要执行 GoReleaser,并在每次我们在 Git 存储库中标记应用程序的新版本时,在 GitHub 上发布一个新的关联版本。

我们开始吧!

我们的 Git 仓库托管在 GitHub 上,因此我们将使用GitHub Actions来实现我们的 CI(持续集成)管道。

创建我们的工作流程:



$ mkdir .github/workflows
$ cd .github/workflows


Enter fullscreen mode Exit fullscreen mode

其中,创建goreleaser.yml包含以下内容的文件:



name: goreleaser

on:
  push:
    tags:
      - '*'

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - name: Set up Go
        uses: actions/setup-go@v2
        with:
          go-version: 1.16
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v2
        with:
          distribution: goreleaser
          version: latest
          args: release --rm-dist
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


Enter fullscreen mode Exit fullscreen mode

此工作流程包含一个任务,我们将检出存储库,使用 GoReleaser 打包我们的应用程序,并生成 GitHub 版本。

为了发布到 GitHub,GoReleaser 需要一个具有相应repo权限的有效 GitHub 令牌。幸运的是,GitHub 会自动创建一个 GITHUB_TOKEN 密钥,供工作流使用。

将修改推送到 Git 仓库后,现在我们可以创建一个 Git 标签并将其推送:



$ git tag -a v1.0.0 -m "First release"

$ git push --tags
Énumération des objets: 1, fait.
Décompte des objets: 100% (1/1), fait.
Écriture des objets: 100% (1/1), 157 octets | 157.00 Kio/s, fait.
Total 1 (delta 0), réutilisés 0 (delta 0), réutilisés du pack 0
To https://github.com/scraly/gophersay.git
 * [new tag]         v1.0.0 -> v1.0.0


Enter fullscreen mode Exit fullscreen mode

让我们进入 GitHub 代码库,然后点击“操作”选项卡,以便查看正在运行、失败和成功的流程:

Gh 行动

完美,我们的工作流程已成功运行。

GitHub 已自动创建了一个新的版本:
生长激素释放

GH发布详情

所以现在,每次我更新我的应用程序并创建一个 Git 标签并推送它时,都会自动创建一个新的(GitHub)GH 版本,其中包含跨平台二进制文件 :-)。

谢谢

如果你喜欢这篇文章/教程和这个可爱的gophersay应用程序,别忘了在GitHub上给它点个星哦 :-)

GitHub 标志 疥癣/地鼠

GopherSay 可以让你显示一只可爱的随机地鼠说的话。

GopherSay


GitHub 发布
代码状态

关于

欢迎来到 GopherSay!

GopherSay 的灵感来源于 Cowsay 程序。

GopherSay 可以让你显示一只可爱的随机地鼠说的话。

安装

适用于 macOS:

brew tap scraly/tools
brew install gophersay

先决条件

安装 Go 语言,最低版本要求为 1.16。

构建应用程序

$ go build -o bin/gophersay main.go

或者

$ task build

运行该应用程序

$ ./bin/gophersay

或者

$ task run

测试该应用

$ ./bin/gophersay Hello Gopher lovers
 ---------------------
< Hello Gopher lovers! >
 ---------------------
        \
         \
                                          ,
                             (%..**,,,,,.& .,,.**
                            ,%,*..,,,. .** **.,*,,**./
                           ./*,,.. .**,.,..,.,**.**..(.
                      .   (@.,*%.**.,,**,.,,%*..**,*,/(
                  ,..../../&&%................%///#.,***.....
                   /....,..........................@*@%...,.,
                     .....................................,
                    &*   #@................................
                 &           ...............................
                #             .........................%       @
               /@     @,      ........................*          *
              *.,            @.......................@    /@@
             /....%        ..........................&
            /........%@@..............................%         %
           ,....................................................*   *   .%
           .........................@,,,,,,,@...................* @   *****#
          *........................@,,,,,,/&(...................  /. ****,**
         ........................@,,,,,,,,,,,, ................/ #/////( ,*,
         //.....................@,,,,,,,,,,,,#................., #//////////
      ...........................,@@@   /  @................../....**//////(
      ...,.........................@@      @.......................///*//*/(
         ...........................&@@@@@@..................   @///////////
         ,..................................................*   @///////&* *
         /..................................................    @/@..,(@.& (
         ,.................................................     @ @/ .(#   .
          ................................................,     @ ,.%(#,**
          .............................................../      @ . @////*/

结论

正如你在本文和之前的文章中看到的,使用 Go 语言可以创建多个不同的应用程序,并实现构建和交叉编译的自动化。
这次我们没有使用 Cobra、Viper 和其他框架,因为我想向你展示,即使不使用它们,我们也能实现一个非常精简的命令行界面 :-)。

我们GopherSay应用的所有 Go语言代码都可以在以下网址找到: https://github.com/scraly/gophersay

在接下来的文章中,我们将用 Go 创建其他类型的应用程序。

希望你会喜欢。

文章来源:https://dev.to/aurelievache/learning-go-by-examples-part-8-automatically-cross-compile-release-your-go-app-457a