mirror of
https://github.com/go-admin-team/go-admin.git
synced 2026-09-21 02:04:09 +00:00
A push to master here does not just build: it pushes an image, runs the migrations and restarts the demo container, so the site takes a short outage each time. The last two merges were markdown only and both paid for it. Beyond the waste, a deploy can fail for reasons unrelated to the change that triggered it - a container that will not come up, a database that is briefly unreachable - and a README edit should not be able to turn the demo red. Only build.yml is filtered. go.yml still builds on every push and pull request, so nothing loses its compile check, and the badge keeps reporting the same workflow it reports today.
147 lines
6.4 KiB
YAML
147 lines
6.4 KiB
YAML
name: Build
|
|
|
|
# Documentation-only changes skip this workflow entirely.
|
|
#
|
|
# A push to master here does not just build - it pushes an image, runs the
|
|
# migrations and restarts the demo container, so the site takes a short outage.
|
|
# Paying that for a README edit is waste at best; at worst a deploy fails for a
|
|
# reason unrelated to anything in the change. Code coverage is unaffected,
|
|
# because go.yml still builds every push and pull request.
|
|
on:
|
|
push:
|
|
branches: [ master ]
|
|
paths-ignore:
|
|
- '**.md'
|
|
- 'docs/**'
|
|
- 'LICENSE*'
|
|
- '.github/ISSUE_TEMPLATE/**'
|
|
pull_request:
|
|
branches: [ master ]
|
|
paths-ignore:
|
|
- '**.md'
|
|
- 'docs/**'
|
|
- 'LICENSE*'
|
|
- '.github/ISSUE_TEMPLATE/**'
|
|
|
|
# One deploy at a time. Two merges seconds apart raced here: both runs did
|
|
# docker rm -f then docker run, the second removed the container the first had
|
|
# just created, and the first's docker run then failed on a name conflict -
|
|
# leaving the demo on the older image with a red deploy.
|
|
concurrency:
|
|
group: deploy-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
IMAGE_NAME: registry.ap-northeast-1.aliyuncs.com/go-admin/go-admin-api # 镜像名称
|
|
TAG: ${{ github.sha }}
|
|
IMAGE_NAME_TAG: registry.ap-northeast-1.aliyuncs.com/go-admin/go-admin-api:${{ github.sha }}
|
|
|
|
jobs:
|
|
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
|
with:
|
|
go-version: 1.26.5
|
|
|
|
- name: Tidy
|
|
run: go mod tidy
|
|
|
|
- name: Build
|
|
run: env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags "sqlite3,json1" --ldflags "-extldflags -static" -o main .
|
|
|
|
# 以下推镜像与重启步骤仅在 master 收到 push 时执行。
|
|
# pull_request 事件同样会触发本工作流,若不加限制,任何指向 master 的
|
|
# PR 一经创建就会把 PR 分支的镜像推上仓库,并直接重启线上 API 服务,
|
|
# 且发生在合并之前。构建与编译校验不受影响,PR 仍会执行。
|
|
- name: Build the Docker image and push
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
run: |
|
|
docker login --username=${{ secrets.DOCKER_USERNAME }} registry.ap-northeast-1.aliyuncs.com --password=${{ secrets.DOCKER_PASSWORD }}
|
|
echo "************ docker login end"
|
|
docker build -t go-admin-api:latest .
|
|
echo "************ docker build end"
|
|
docker tag go-admin-api ${{ env.IMAGE_NAME_TAG }}
|
|
echo "************ docker tag end"
|
|
docker images
|
|
echo "************ docker images end"
|
|
docker push ${{ env.IMAGE_NAME_TAG }} # 推送
|
|
echo "************ docker push end"
|
|
|
|
- name: Restart server # 第五步,重启服务
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5
|
|
env:
|
|
GITHUB_SHA_X: ${GITHUB_SHA}
|
|
with:
|
|
host: ${{ secrets.SSH_HOST }} # 下面三个配置与上一步类似
|
|
username: ${{ secrets.SSH_USERNAME }}
|
|
key: ${{ secrets.DEPLOY_KEY }}
|
|
# 重启的脚本,根据自身情况做相应改动,一般要做的是migrate数据库以及重启服务器
|
|
#
|
|
# 配置从宿主机挂载,不使用镜像里的那份:演示站连的是托管数据库,
|
|
# 而 config/settings.demo.yml 会随仓库公开、也会打进镜像,凭据不能写在那里。
|
|
# 镜像里那份保持 sqlite,供 clone 仓库的人开箱即用。
|
|
#
|
|
# 路径本身走 secret:它不是凭据,但本仓库公开,没有理由把服务器的
|
|
# 目录结构一并公布。DEMO_CONFIG_PATH 指向宿主机上那份配置。
|
|
#
|
|
# 顺序是有意的:迁移先跑,跑不过就保持现有版本不动;
|
|
# 旧容器改名保留而不是删除,新容器不健康时能原样恢复。
|
|
# 健康检查两条都要过——HTTP 活着不代表数据库通了。
|
|
script: |
|
|
set -u
|
|
CFG="${{ secrets.DEMO_CONFIG_PATH }}"
|
|
IMG="${{ env.IMAGE_NAME_TAG }}"
|
|
NAME=go-admin-api
|
|
PREV="$NAME-prev"
|
|
|
|
test -f "$CFG" || { echo "宿主机配置缺失,中止部署"; exit 1; }
|
|
|
|
sudo docker login --username=${{ secrets.DOCKER_USERNAME }} registry.ap-northeast-1.aliyuncs.com --password=${{ secrets.DOCKER_PASSWORD }}
|
|
sudo docker pull "$IMG" || { echo "拉取镜像失败,中止部署"; exit 1; }
|
|
|
|
# 迁移用新镜像跑。失败时线上仍是旧版本配旧 schema,是自洽的;
|
|
# 硬切过去才会得到代码与表对不上的服务。
|
|
if ! sudo docker run --rm -v "$CFG":/config/settings.yml:ro "$IMG" \
|
|
/main migrate -c /config/settings.yml; then
|
|
echo "迁移失败,保持现有版本"; exit 1
|
|
fi
|
|
|
|
if sudo docker ps -a --format '{{.Names}}' | grep -qx "$NAME"; then
|
|
sudo docker rm -f "$PREV" >/dev/null 2>&1 || true
|
|
sudo docker rename "$NAME" "$PREV"
|
|
sudo docker stop "$PREV" >/dev/null
|
|
fi
|
|
|
|
sudo docker run -d -p 8000:8000 \
|
|
-v "$CFG":/config/settings.yml:ro \
|
|
--name "$NAME" "$IMG"
|
|
|
|
ok=0
|
|
for i in $(seq 1 20); do
|
|
sleep 3
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' -m 5 http://127.0.0.1:8000/api/v1/captcha 2>/dev/null || true)
|
|
if [ "$code" = "200" ] && sudo docker logs "$NAME" 2>&1 | grep -q 'connect success'; then
|
|
ok=1; echo "健康检查通过(第 $i 次探测)"; break
|
|
fi
|
|
done
|
|
|
|
if [ "$ok" = "1" ]; then
|
|
sudo docker rm -f "$PREV" >/dev/null 2>&1 || true
|
|
else
|
|
echo "健康检查失败,回滚到上一版本"
|
|
sudo docker logs --tail 40 "$NAME" 2>&1 || true
|
|
sudo docker rm -f "$NAME" >/dev/null 2>&1 || true
|
|
if sudo docker ps -a --format '{{.Names}}' | grep -qx "$PREV"; then
|
|
sudo docker rename "$PREV" "$NAME"
|
|
sudo docker start "$NAME" >/dev/null
|
|
echo "已恢复"
|
|
fi
|
|
exit 1
|
|
fi
|