206 lines
6.6 KiB
YAML
206 lines
6.6 KiB
YAML
# 通用 Go + Vue 项目 CI(Gitea Actions)
|
||
#
|
||
# 复制整个 .gitea/ 到任意仓库即可使用,约定:
|
||
# - 仓库根目录有 go.mod、Dockerfile
|
||
# - 前端(可选)默认在 web/,含 package.json
|
||
#
|
||
# 前置条件:
|
||
# 1. 在线 act_runner(runs-on: ubuntu-latest,见 act-runner/README.md)
|
||
# 2. 仓库 Secret:REGISTRY_TOKEN(PAT,write:package 权限)
|
||
#
|
||
# 可选:仓库 Settings → Actions → Variables 覆盖下方空默认值
|
||
# REGISTRY、IMAGE_NAME、DOCKERFILE、DOCKER_PLATFORMS、WEB_DIR、GO_TEST_SCOPE、GOPROXY、GOSUMDB
|
||
|
||
name: CI
|
||
|
||
on:
|
||
push:
|
||
branches: [main, master]
|
||
tags: ['v*']
|
||
pull_request:
|
||
|
||
env:
|
||
REGISTRY: ${{ vars.REGISTRY }}
|
||
IMAGE_NAME: ${{ vars.IMAGE_NAME }}
|
||
DOCKERFILE: ${{ vars.DOCKERFILE }}
|
||
DOCKER_PLATFORMS: ${{ vars.DOCKER_PLATFORMS }}
|
||
WEB_DIR: ${{ vars.WEB_DIR }}
|
||
GO_TEST_SCOPE: ${{ vars.GO_TEST_SCOPE }}
|
||
DOCKER_IMAGE_PREFIX: ${{ vars.DOCKER_IMAGE_PREFIX }}
|
||
GOPROXY: ${{ vars.GOPROXY }}
|
||
GOSUMDB: ${{ vars.GOSUMDB }}
|
||
|
||
jobs:
|
||
test:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Setup Go
|
||
shell: bash
|
||
env:
|
||
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||
run: |
|
||
set -euo pipefail
|
||
export GOPROXY="${GOPROXY:-https://goproxy.cn,direct}"
|
||
export GOSUMDB="${GOSUMDB:-sum.golang.google.cn}"
|
||
|
||
GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}')
|
||
ARCH=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')
|
||
TAR="go${GO_VERSION}.linux-${ARCH}.tar.gz"
|
||
|
||
if command -v go >/dev/null 2>&1 && go version | grep -q "go${GO_VERSION} "; then
|
||
go version
|
||
else
|
||
downloaded=0
|
||
for url in \
|
||
"https://mirrors.aliyun.com/golang/${TAR}" \
|
||
"https://golang.google.cn/dl/${TAR}" \
|
||
"https://go.dev/dl/${TAR}"; do
|
||
echo "trying ${url}"
|
||
if curl -fsSL --connect-timeout 20 --retry 3 --retry-delay 5 --max-time 600 \
|
||
"$url" -o /tmp/go.tgz; then
|
||
downloaded=1
|
||
break
|
||
fi
|
||
done
|
||
if [ "$downloaded" -ne 1 ]; then
|
||
echo "failed to download Go ${GO_VERSION}" >&2
|
||
exit 1
|
||
fi
|
||
rm -rf /usr/local/go
|
||
tar -C /usr/local -xzf /tmp/go.tgz
|
||
fi
|
||
echo "/usr/local/go/bin" >> "$GITHUB_PATH"
|
||
/usr/local/go/bin/go version
|
||
|
||
- name: Run Go tests
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
export GOPROXY="${GOPROXY:-https://goproxy.cn,direct}"
|
||
export GOSUMDB="${GOSUMDB:-sum.golang.google.cn}"
|
||
scope="${GO_TEST_SCOPE:-./...}"
|
||
go test "${scope}"
|
||
|
||
- name: Build web (optional)
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
web_dir="${WEB_DIR:-web}"
|
||
if [ ! -f "${web_dir}/package.json" ]; then
|
||
echo "skip: ${web_dir}/package.json not found"
|
||
exit 0
|
||
fi
|
||
if ! command -v npm >/dev/null 2>&1; then
|
||
echo "skip: npm not in PATH (frontend will be built in Docker stage)"
|
||
exit 0
|
||
fi
|
||
cd "${web_dir}"
|
||
if [ -f package-lock.json ]; then
|
||
npm ci
|
||
else
|
||
npm install
|
||
fi
|
||
npm run build
|
||
|
||
docker:
|
||
runs-on: ubuntu-latest
|
||
container:
|
||
image: catthehacker/ubuntu:act-22.04
|
||
needs: test
|
||
if: gitea.event_name == 'push'
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Setup Docker
|
||
run: |
|
||
set -eux
|
||
if ! command -v docker >/dev/null 2>&1; then
|
||
apt-get update
|
||
DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io
|
||
fi
|
||
if [ ! -S /var/run/docker.sock ]; then
|
||
echo "ERROR: /var/run/docker.sock 未挂载到 job 容器。" >&2
|
||
echo "请在 act_runner config.yaml 中配置:" >&2
|
||
echo " container.options: -v /var/run/docker.sock:/var/run/docker.sock" >&2
|
||
echo " container.valid_volumes: [/var/run/docker.sock]" >&2
|
||
exit 1
|
||
fi
|
||
docker info
|
||
|
||
- name: Log in to Container Registry
|
||
shell: bash
|
||
env:
|
||
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||
run: |
|
||
set -euo pipefail
|
||
registry="${REGISTRY:-}"
|
||
if [ -z "$registry" ]; then
|
||
registry=$(echo "${GITEA_SERVER_URL}" | sed -e 's|^https://||' -e 's|^http://||' -e 's|/.*||')
|
||
fi
|
||
echo "${{ secrets.REGISTRY_TOKEN }}" | \
|
||
docker login "${registry}" -u "${{ gitea.actor }}" --password-stdin
|
||
|
||
- name: Build and push
|
||
shell: bash
|
||
env:
|
||
GITEA_SHA: ${{ gitea.sha }}
|
||
GITEA_REF: ${{ gitea.ref }}
|
||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||
GITEA_REPOSITORY_OWNER: ${{ gitea.repository_owner }}
|
||
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
registry="${REGISTRY:-}"
|
||
if [ -z "$registry" ]; then
|
||
registry=$(echo "${GITEA_SERVER_URL}" | sed -e 's|^https://||' -e 's|^http://||' -e 's|/.*||')
|
||
fi
|
||
|
||
image_name="${IMAGE_NAME:-}"
|
||
if [ -z "$image_name" ]; then
|
||
image_name=$(echo "${GITEA_REPOSITORY##*/}" | tr '[:upper:]' '[:lower:]')
|
||
fi
|
||
|
||
dockerfile="${DOCKERFILE:-Dockerfile}"
|
||
platforms="${DOCKER_PLATFORMS:-linux/amd64,linux/arm64}"
|
||
|
||
image="${registry}/${GITEA_REPOSITORY_OWNER}/${image_name}"
|
||
tags="${image}:sha-${GITEA_SHA:0:7}"
|
||
case "${GITEA_REF}" in
|
||
refs/heads/main|refs/heads/master)
|
||
tags="${tags},${image}:latest"
|
||
;;
|
||
refs/tags/v*)
|
||
tags="${tags},${image}:${GITEA_REF#refs/tags/}"
|
||
;;
|
||
esac
|
||
|
||
builder_id="gitea-buildx-${GITEA_REPOSITORY//\//-}"
|
||
|
||
docker run --privileged --rm tonistiigi/binfmt --install all 2>/dev/null || true
|
||
docker buildx create --name "${builder_id}" --use 2>/dev/null || docker buildx use "${builder_id}"
|
||
docker buildx inspect --bootstrap
|
||
|
||
tag_args=()
|
||
IFS=',' read -r -a tag_list <<< "$tags"
|
||
for t in "${tag_list[@]}"; do
|
||
tag_args+=(-t "$t")
|
||
done
|
||
|
||
build_args=()
|
||
if [ -n "${DOCKER_IMAGE_PREFIX:-}" ]; then
|
||
build_args+=(--build-arg "IMAGE_PREFIX=${DOCKER_IMAGE_PREFIX}")
|
||
fi
|
||
|
||
docker buildx build \
|
||
--platform "${platforms}" \
|
||
-f "${dockerfile}" \
|
||
"${build_args[@]}" \
|
||
"${tag_args[@]}" \
|
||
--push \
|
||
.
|