# Gitea Actions CI 模板（Go + Vue）

复制整个 `.gitea/` 目录到新仓库即可启用 CI：**可选 go test** + **Docker 构建/推送**（Go 编译与 Vue 构建在 Dockerfile 内完成）。

## 项目约定

| 路径 | 说明 |
|------|------|
| `go.mod` | 仓库根目录 |
| `Dockerfile` | 仓库根目录，多阶段构建（含前端 `web/`） |
| `web/` | 可选；由 Dockerfile 内 `npm run build` 处理，CI 不再单独构建 |

## Dockerfile 要求

工作流的 **Build image** 步骤在仓库根目录（或 Variable `DOCKERFILE` 指定路径）执行 `docker buildx build`，**不会**在 CI 里单独装 Node / 跑 `npm`。因此 Dockerfile 必须能独立完成构建与（运行时）启动。

### 基本要求

| 项 | 要求 |
|----|------|
| 位置 | 默认仓库根目录 `Dockerfile`；其他路径用 Variable `DOCKERFILE` |
| 构建上下文 | 仓库根目录 `.`（整个仓库会被 `COPY` 进镜像） |
| Go 版本 | `go.mod` 里 `go` 行与 `golang` 基础镜像大版本一致 |
| 多架构 | `go build` 须使用 `GOARCH="$TARGETARCH"`（buildx 注入）；推荐 `CGO_ENABLED=0` |
| Go 模块代理 | **必须**在镜像内显式设置 `GOPROXY` / `GOSUMDB`（见下）；容器内不会自动读取 `go.env` |
| 基础镜像加速 | 支持构建参数 `IMAGE_PREFIX`（CI 默认 `docker.1panel.live/library/`） |
| 前端（可选） | 存在 `web/` 时在 Dockerfile 内完成 `npm ci` + `npm run build` |

### CI 自动传入的构建参数

| 参数 | 默认 | 说明 |
|------|------|------|
| `IMAGE_PREFIX` | `docker.1panel.live/library/` | 基础镜像前缀；`hub` 表示 Docker Hub |
| `GOPROXY` | `https://goproxy.cn,direct` | 与 workflow / Variable 一致 |
| `GOSUMDB` | `sum.golang.google.cn` | checksum 数据库 |

buildx 还会注入 `TARGETARCH`、`BUILDPLATFORM` 等，无需在 workflow 里写。

### 推荐：Go + Vue 多阶段模板

```dockerfile
# syntax=docker/dockerfile:1
ARG IMAGE_PREFIX=

# 1) 前端（无 web/ 时可删整个 stage，并去掉 go-builder 里 COPY dist）
FROM --platform=$BUILDPLATFORM ${IMAGE_PREFIX}node:20-alpine AS web-builder
WORKDIR /src/web
COPY web/package.json web/package-lock.json web/.npmrc ./
RUN npm ci
COPY web/ ./
RUN npm run build

# 2) Go 编译
FROM --platform=$BUILDPLATFORM ${IMAGE_PREFIX}golang:1.25-alpine AS go-builder
ARG TARGETARCH
ARG GOPROXY=https://goproxy.cn,direct
ARG GOSUMDB=sum.golang.google.cn
ENV GOPROXY=${GOPROXY} GOSUMDB=${GOSUMDB}

WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download

COPY cmd/ ./cmd/
COPY internal/ ./internal/
# 若静态资源嵌入 Go：COPY --from=web-builder /src/web/dist/ ./path/to/static/

RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    CGO_ENABLED=0 GOOS=linux GOARCH="$TARGETARCH" \
    go build -ldflags="-w -s" -o /app ./cmd/yourapp

# 3) 运行镜像
FROM ${IMAGE_PREFIX}alpine:3.20
RUN apk add --no-cache ca-certificates tzdata
COPY --from=go-builder /app /usr/local/bin/yourapp
ENTRYPOINT ["/usr/local/bin/yourapp"]
```

按项目调整：`./cmd/yourapp`、静态资源路径、运行用户、`EXPOSE` / `VOLUME` 等。

### 仅 Go（无前端）

删除 `web-builder` stage；`go-builder` 中不要 `COPY` 前端产物；其余 `GOPROXY` / `TARGETARCH` / `IMAGE_PREFIX` 要求相同。

### 前端 npm 源（国内）

在 `web/.npmrc` 配置 registry，并在 Dockerfile 里与 `package.json` 一并 `COPY`：

```ini
registry=https://registry.npmmirror.com
```

### 本地验证（与 CI 一致）

```bash
docker buildx build --platform linux/amd64 \
  --build-arg IMAGE_PREFIX=docker.1panel.live/library/ \
  --build-arg GOPROXY=https://goproxy.cn,direct \
  -f Dockerfile -t myapp:test .
```

### 常见 Dockerfile 构建错误

| 报错 | 原因 | 处理 |
|------|------|------|
| `proxy.golang.org` timeout | 镜像内未设 `ENV GOPROXY` | go-builder 阶段加 `ARG`/`ENV GOPROXY` |
| `node` / Vite 版本不符 | 基础镜像 Node 过旧 | 使用 `node:20-alpine` 及以上 |
| 某架构 build 失败 | 未使用 `TARGETARCH` | `GOARCH="$TARGETARCH"` |
| 拉基础镜像 timeout | Hub 不可达 | `--build-arg IMAGE_PREFIX=docker.1panel.live/library/` 或 1Panel 配镜像加速 |

## 一次性配置

1. **Runner**：部署 act_runner，标签含 `ubuntu-latest`，并挂载 `docker.sock`（见 [act-runner/README.md](act-runner/README.md)）。
2. **Secret**：仓库 Settings → Actions → Secrets，添加 `REGISTRY_TOKEN`（PAT，`write:package` 权限）。
3. **Variable（推荐）**：若 runner 与 Gitea 同机、或 `gitea.server_url` 为内网地址，必须设置 `REGISTRY=你的公网域名`（如 `git.example.com`，**不要**填 `172.17.0.1:13827`）。
4. **Gitea Registry**：服务端 `[packages] ENABLED = true`，`ROOT_URL` 正确；穿透场景建议 `PUBLIC_URL_DETECTION = never`（Gitea 1.26+）。

`REGISTRY` 未设置时，会从 `GITEA_ROOT_URL` 或 `gitea.server_url` 推断；均为内网地址时 workflow 会提前失败并提示。

## 可选 Variables

仓库 Settings → Actions → Variables（留空则用默认值）：

| 变量 | 默认 | 说明 |
|------|------|------|
| `REGISTRY` | 见下方推断顺序 | **公网** Registry 主机名，如 `git.example.com`（勿用内网 IP:13827） |
| `GITEA_ROOT_URL` | （空） | 当 `gitea.server_url` 为内网时，可设 `https://git.example.com/` |
| `IMAGE_NAME` | 仓库名小写 | 镜像名，非 owner/repo 全路径 |
| `DOCKERFILE` | `Dockerfile` | Dockerfile 路径 |
| `DOCKER_PLATFORMS` | `linux/amd64,linux/arm64` | push 时 buildx 平台 |
| `GO_TEST_SCOPE` | `./...` | `go test` 包路径 |
| `RUN_GO_TEST` | （空，即运行） | 设 `false` 跳过 go test，仅 Docker 构建 |
| `DOCKER_IMAGE_PREFIX` | `1panel` | 基础镜像前缀；可选 `hub` / `daocloud` |
| `GOPROXY` | `https://goproxy.cn,direct` | Go 模块代理 |
| `GOSUMDB` | `sum.golang.google.cn` | Go checksum 数据库 |

## 触发与镜像 tag

- **pull_request**：go test（可关）+ 单架构 `docker build` 验证（不推送）
- **push main/master**：go test + 多架构构建并推送 `:latest`、`:sha-xxxxxxx`
- **push tag v\***：额外推送 `:v1.2.3` 等

示例（仓库 `rose_cat707/Prism`，Gitea 在 `git.example.com`）：

```text
git.example.com/rose_cat707/prism:latest
git.example.com/rose_cat707/prism:sha-35b3b48
```

## 目录结构

```text
.gitea/
├── README.md                 # 本文件
├── workflows/
│   └── ci.yml                # 主工作流
└── act-runner/               # runner 部署参考（可选）
    ├── README.md
    ├── config.yaml
    ├── docker-compose.yml
    └── .env.example
```

## 本地验证 Registry

```bash
host=$(echo "https://你的-gitea-地址/" | sed -e 's|^https://||' -e 's|/.*||')
curl -s -D - "https://${host}/v2/" -o /dev/null | grep -i www-authenticate
docker pull "${host}/owner/image:latest"   # 公开 Registry 无需 login
```

推送镜像（CI）仍需仓库 Secret `REGISTRY_TOKEN`（`write:package`）。仅拉取公开包不需要登录。

`realm` 应指向公网 Gitea 域名，而非 `127.0.0.1`。

## 常见 Registry 错误

| 报错 | 原因 | 处理 |
|------|------|------|
| `Get "https://172.17.0.1:13827/v2/"` HTTP/HTTPS | Variable `REGISTRY` 或 `gitea.server_url` 为内网地址 | 设 `REGISTRY=git.example.com` |
| token 指向 `127.0.0.1` | Gitea `realm` 配置错误 | `PUBLIC_URL_DETECTION=never` + 正确 `ROOT_URL` |
