- 新增:统一的 git 命令文档(add/commit/push/pull 等) - 新增:整合的 Gitea 技能文档(API、运行器、工作流等) - 新增:工作流模板(Android、Go、Node.js 等) - 移除:已弃用的旧命令脚本和发布脚本 - 改进:.gitignore 添加敏感文件保护规则 - 改进:AGENTS.md 完善了开发规范和示例 此次重组统一了命令和技能的文档结构,便于后续维护和扩展。
326 lines
9.1 KiB
Markdown
326 lines
9.1 KiB
Markdown
# Go 后端服务 Workflow 模板
|
||
|
||
适用于 Go 后端 API 服务、微服务、CLI 工具的 CI/CD workflow。
|
||
|
||
## 适用场景
|
||
|
||
- Go HTTP API 服务
|
||
- gRPC 微服务
|
||
- CLI 工具
|
||
- 需要构建 Docker 镜像的 Go 项目
|
||
|
||
## 环境要求
|
||
|
||
| 依赖 | Runner 要求 |
|
||
|------|------------|
|
||
| Go 1.21+ | Runner 主机已安装 |
|
||
| Docker | Runner 主机已安装 |
|
||
|
||
## Workflow 骨架模板
|
||
|
||
```yaml
|
||
name: Go Backend - Build & Publish
|
||
|
||
on:
|
||
push:
|
||
paths:
|
||
- 'your-service/**' # 修改为实际目录
|
||
- '.gitea/workflows/your-service.yml'
|
||
tags:
|
||
- 'your-service-*' # 修改为实际 tag 前缀
|
||
|
||
concurrency:
|
||
group: ${{ github.workflow }}-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
env:
|
||
SERVICE_PREFIX: your-service # 修改为实际服务名
|
||
SERVICE_DIR: your-service # 修改为实际目录名
|
||
GOPROXY: https://goproxy.cn,direct
|
||
|
||
jobs:
|
||
build-and-publish:
|
||
name: Build & Publish
|
||
runs-on: darwin-arm64
|
||
permissions:
|
||
contents: read
|
||
packages: write
|
||
|
||
outputs:
|
||
binary_name: ${{ steps.vars.outputs.binary_name }}
|
||
git_tag: ${{ steps.vars.outputs.git_tag }}
|
||
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
- name: Verify Go environment
|
||
run: |
|
||
go version
|
||
echo "GOPATH=$(go env GOPATH)"
|
||
|
||
- name: Get go-hashfiles
|
||
id: hash-go
|
||
working-directory: ${{ env.SERVICE_DIR }}
|
||
run: |
|
||
HASH=$(sha256sum go.mod go.sum | sha256sum | awk '{print $1}' | head -c 16)
|
||
echo "hash=${HASH}" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: Cache Go modules
|
||
uses: https://github.com/actions/cache@v3
|
||
with:
|
||
path: |
|
||
~/go/pkg/mod
|
||
~/.cache/go-build
|
||
key: go-${{ env.SERVICE_PREFIX }}-${{ steps.hash-go.outputs.hash }}
|
||
restore-keys: go-${{ env.SERVICE_PREFIX }}-
|
||
|
||
- name: Set variables
|
||
id: vars
|
||
run: |
|
||
git_tag=$(git describe --tags --abbrev=0 --always)
|
||
registry=$(echo ${{ github.server_url }} | cut -d '/' -f 3)
|
||
binary_name="${{ github.event.repository.name }}-${{ env.SERVICE_PREFIX }}"
|
||
image_repo="${{ github.event.repository.name }}"
|
||
|
||
{
|
||
echo "git_tag=${git_tag}"
|
||
echo "registry=${registry}"
|
||
echo "binary_name=${binary_name}"
|
||
echo "image_repo=${image_repo}"
|
||
echo "latest_tag=${{ env.SERVICE_PREFIX }}-latest"
|
||
} >> $GITHUB_ENV
|
||
|
||
echo "binary_name=${binary_name}" >> $GITHUB_OUTPUT
|
||
echo "git_tag=${git_tag}" >> $GITHUB_OUTPUT
|
||
|
||
- name: Build
|
||
working-directory: ${{ env.SERVICE_DIR }}
|
||
env:
|
||
CGO_ENABLED: 0
|
||
GOOS: linux
|
||
GOARCH: amd64
|
||
run: |
|
||
# ============================================
|
||
# 用户自定义构建步骤(按项目需求修改)
|
||
# ============================================
|
||
|
||
# 代码生成(按需启用)
|
||
# go generate ./...
|
||
# go tool ent generate ./schema
|
||
# go tool wire ./...
|
||
# go tool oapi-codegen -config oapi.yaml api.yaml
|
||
# go tool stringer -type=MyType ./...
|
||
|
||
# 测试(按需启用)
|
||
# go test ./...
|
||
# go vet ./...
|
||
|
||
# 构建(必须)
|
||
go build -o ${{ env.binary_name }} \
|
||
-ldflags '-s -w -X main.GitTag=${{ env.git_tag }}'
|
||
|
||
chmod +x ${{ env.binary_name }}
|
||
|
||
- name: Upload artifact
|
||
uses: actions/upload-artifact@v3
|
||
with:
|
||
name: ${{ env.SERVICE_PREFIX }}-binary-linux-amd64
|
||
path: ${{ env.SERVICE_DIR }}/${{ env.binary_name }}
|
||
|
||
- name: Docker - Login
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ${{ env.registry }}
|
||
username: ${{ vars.REGISTRY_USERNAME }}
|
||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||
|
||
- name: Docker - Setup Buildx
|
||
uses: docker/setup-buildx-action@v3
|
||
|
||
- name: Docker - Build & Push
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ${{ env.SERVICE_DIR }}
|
||
file: ./${{ env.SERVICE_DIR }}/Dockerfile.ci
|
||
push: true
|
||
platforms: linux/amd64
|
||
tags: |
|
||
${{ env.registry }}/${{ github.repository_owner }}/${{ env.image_repo }}:${{ env.latest_tag }}
|
||
${{ env.registry }}/${{ github.repository_owner }}/${{ env.image_repo }}:${{ env.git_tag }}
|
||
cache-from: type=registry,ref=${{ env.registry }}/${{ github.repository_owner }}/${{ env.image_repo }}:buildcache
|
||
cache-to: type=registry,ref=${{ env.registry }}/${{ github.repository_owner }}/${{ env.image_repo }}:buildcache,mode=max
|
||
|
||
- name: Notify
|
||
if: always()
|
||
continue-on-error: true
|
||
env:
|
||
WEBHOOK_URL: ${{ vars.WEBHOOK_URL }}
|
||
run: |
|
||
status="${{ job.status }}"
|
||
[ "$status" = "success" ] && status_text="Build Success" || status_text="Build Failed"
|
||
|
||
curl -s -H "Content-Type: application/json" -X POST -d \
|
||
"{\"msg_type\":\"text\",\"content\":{\"text\":\"${{ env.binary_name }} ${status_text}\"}}" \
|
||
"$WEBHOOK_URL" || true
|
||
|
||
release:
|
||
name: Create Release
|
||
runs-on: darwin-arm64
|
||
needs: build-and-publish
|
||
if: startsWith(github.ref, 'refs/tags/')
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
- name: Download artifact
|
||
uses: actions/download-artifact@v3
|
||
with:
|
||
name: ${{ env.SERVICE_PREFIX }}-binary-linux-amd64
|
||
path: dist
|
||
|
||
- name: Create Release
|
||
env:
|
||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||
BINARY_NAME: ${{ needs.build-and-publish.outputs.binary_name }}
|
||
run: |
|
||
git_tag=$(git describe --tags --abbrev=0)
|
||
api_url="${{ github.server_url }}/api/v1"
|
||
repo="${{ github.repository }}"
|
||
|
||
# 生成校验和
|
||
cd dist
|
||
sha256sum "${BINARY_NAME}" > "${BINARY_NAME}.sha256"
|
||
cd ..
|
||
|
||
# 创建 Release
|
||
release_id=$(curl -s -X POST \
|
||
-H "Authorization: token $GITEA_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag}\"}" \
|
||
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
||
|
||
# 上传二进制文件
|
||
curl -s -X POST \
|
||
-H "Authorization: token $GITEA_TOKEN" \
|
||
-F "attachment=@dist/${BINARY_NAME}" \
|
||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||
|
||
# 上传校验和
|
||
curl -s -X POST \
|
||
-H "Authorization: token $GITEA_TOKEN" \
|
||
-F "attachment=@dist/${BINARY_NAME}.sha256" \
|
||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||
```
|
||
|
||
---
|
||
|
||
## Dockerfile.ci 模板
|
||
|
||
```dockerfile
|
||
FROM alpine:latest
|
||
|
||
# 安装时区数据和证书
|
||
RUN apk add --no-cache tzdata ca-certificates
|
||
|
||
# 设置时区
|
||
ENV TZ=Asia/Shanghai
|
||
|
||
# 复制构建好的二进制文件
|
||
# 注意:需要在 docker build 时通过 --build-arg 传递 BINARY_NAME
|
||
# 或者直接写死二进制文件名
|
||
ARG BINARY_NAME=server
|
||
COPY ${BINARY_NAME} /app/server
|
||
|
||
WORKDIR /app
|
||
|
||
# 健康检查(按需修改端口和路径)
|
||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
|
||
|
||
EXPOSE 8080
|
||
|
||
ENTRYPOINT ["/app/server"]
|
||
```
|
||
|
||
---
|
||
|
||
## 缓存配置
|
||
|
||
### 缓存路径
|
||
|
||
```yaml
|
||
path: |
|
||
~/go/pkg/mod # Go 模块缓存
|
||
~/.cache/go-build # Go 构建缓存
|
||
```
|
||
|
||
### Key 计算
|
||
|
||
```bash
|
||
HASH=$(sha256sum go.mod go.sum | sha256sum | awk '{print $1}' | head -c 16)
|
||
```
|
||
|
||
---
|
||
|
||
## 构建参数说明
|
||
|
||
```bash
|
||
CGO_ENABLED=0 # 禁用 CGO,生成静态链接二进制
|
||
GOOS=linux # 目标操作系统
|
||
GOARCH=amd64 # 目标架构
|
||
|
||
-ldflags '-s -w' # 去除符号表和调试信息,减小体积
|
||
-X main.GitTag=... # 注入版本信息
|
||
```
|
||
|
||
---
|
||
|
||
## Secrets 配置
|
||
|
||
| Secret | 用途 |
|
||
|--------|------|
|
||
| `REGISTRY_PASSWORD` | Docker Registry 密码 |
|
||
| `RELEASE_TOKEN` | Gitea API 令牌(创建 Release) |
|
||
|
||
---
|
||
|
||
## 常见代码生成工具
|
||
|
||
根据项目使用的框架,在 Build 步骤中添加相应的生成命令:
|
||
|
||
| 框架/工具 | 命令 |
|
||
|----------|------|
|
||
| Ent (ORM) | `go tool ent generate ./schema` |
|
||
| Wire (DI) | `go tool wire ./...` |
|
||
| oapi-codegen | `go tool oapi-codegen -config oapi.yaml api.yaml` |
|
||
| Stringer | `go tool stringer -type=MyType ./...` |
|
||
| Protobuf | `protoc --go_out=. --go-grpc_out=. *.proto` |
|
||
| go generate | `go generate ./...` |
|
||
|
||
---
|
||
|
||
## 使用步骤
|
||
|
||
1. 复制上方 workflow 模板到 `.gitea/workflows/your-service.yml`
|
||
2. 修改 `SERVICE_PREFIX` 和 `SERVICE_DIR` 为实际值
|
||
3. 修改 `paths` 和 `tags` 触发条件
|
||
4. 根据项目需求填充 "用户自定义构建步骤" 部分
|
||
5. 创建 `Dockerfile.ci` 文件
|
||
6. 配置 Secrets
|
||
7. 推送代码触发 workflow
|
||
|
||
---
|
||
|
||
## 版本发布
|
||
|
||
```bash
|
||
# 创建并推送 tag
|
||
git tag your-service-1.0.0
|
||
git push origin your-service-1.0.0
|
||
```
|