ci: 优化 workflow 并行度与产物传递

- check 与 build-linux 改为并行执行,节省约 10min
- 新增 upload-artifact / download-artifact,deploy-mcp 直接复用二进制,免重复编译(节省约 15min)
- check / build 缓存加入 target/ 目录,加速增量编译
- 提取 MUSL_TARGET 全局变量,消除 x86_64-unknown-linux-musl 硬编码
- publish-release 增加 check 结果检查,质量失败时不发布 Release
- 移除 build-linux 冗余飞书通知,publish-release 汇总已覆盖

Made-with: Cursor
This commit is contained in:
voson
2026-03-21 10:07:29 +08:00
parent a44c8ebf08
commit ee028d45c3

View File

@@ -7,7 +7,6 @@ on:
- 'crates/**' - 'crates/**'
- 'Cargo.toml' - 'Cargo.toml'
- 'Cargo.lock' - 'Cargo.lock'
# systemd / 部署模板变更也应跑构建(产物无变时可快速跳过 check
- 'deploy/**' - 'deploy/**'
- '.gitea/workflows/**' - '.gitea/workflows/**'
@@ -25,6 +24,7 @@ env:
CARGO_NET_RETRY: 10 CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
RUST_BACKTRACE: short RUST_BACKTRACE: short
MUSL_TARGET: x86_64-unknown-linux-musl
jobs: jobs:
version: version:
@@ -138,6 +138,7 @@ jobs:
echo "release_id=" >> "$GITHUB_OUTPUT" echo "release_id=" >> "$GITHUB_OUTPUT"
fi fi
# check 与 build-linux 并行执行,互不阻塞
check: check:
name: 质量检查 (fmt / clippy / test) name: 质量检查 (fmt / clippy / test)
needs: [version] needs: [version]
@@ -165,6 +166,7 @@ jobs:
~/.cargo/registry/index ~/.cargo/registry/index
~/.cargo/registry/cache ~/.cargo/registry/cache
~/.cargo/git/db ~/.cargo/git/db
target
key: cargo-check-${{ env.RUST_TOOLCHAIN }}-${{ hashFiles('Cargo.lock') }} key: cargo-check-${{ env.RUST_TOOLCHAIN }}-${{ hashFiles('Cargo.lock') }}
restore-keys: | restore-keys: |
cargo-check-${{ env.RUST_TOOLCHAIN }}- cargo-check-${{ env.RUST_TOOLCHAIN }}-
@@ -175,8 +177,8 @@ jobs:
- run: cargo test --locked - run: cargo test --locked
build-linux: build-linux:
name: Build Linux (secrets-mcp, musl) name: Build Linux (musl)
needs: [version, check] needs: [version]
runs-on: debian runs-on: debian
timeout-minutes: 25 timeout-minutes: 25
steps: steps:
@@ -191,7 +193,7 @@ jobs:
source "$HOME/.cargo/env" 2>/dev/null || true source "$HOME/.cargo/env" 2>/dev/null || true
rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal
rustup default "${RUST_TOOLCHAIN}" rustup default "${RUST_TOOLCHAIN}"
rustup target add x86_64-unknown-linux-musl --toolchain "${RUST_TOOLCHAIN}" rustup target add ${{ env.MUSL_TARGET }} --toolchain "${RUST_TOOLCHAIN}"
rustc -V rustc -V
cargo -V cargo -V
@@ -204,15 +206,23 @@ jobs:
~/.cargo/registry/index ~/.cargo/registry/index
~/.cargo/registry/cache ~/.cargo/registry/cache
~/.cargo/git/db ~/.cargo/git/db
key: cargo-x86_64-unknown-linux-musl-${{ env.RUST_TOOLCHAIN }}-${{ hashFiles('Cargo.lock') }} target
key: cargo-${{ env.MUSL_TARGET }}-${{ env.RUST_TOOLCHAIN }}-${{ hashFiles('Cargo.lock') }}
restore-keys: | restore-keys: |
cargo-x86_64-unknown-linux-musl-${{ env.RUST_TOOLCHAIN }}- cargo-${{ env.MUSL_TARGET }}-${{ env.RUST_TOOLCHAIN }}-
cargo-x86_64-unknown-linux-musl- cargo-${{ env.MUSL_TARGET }}-
- name: 构建 secrets-mcp (musl) - name: 构建 secrets-mcp (musl)
run: | run: |
cargo build --release --locked --target x86_64-unknown-linux-musl -p secrets-mcp cargo build --release --locked --target ${{ env.MUSL_TARGET }} -p secrets-mcp
strip target/x86_64-unknown-linux-musl/release/${{ env.MCP_BINARY }} strip target/${{ env.MUSL_TARGET }}/release/${{ env.MCP_BINARY }}
- name: 上传构建产物
uses: actions/upload-artifact@v3
with:
name: ${{ env.MCP_BINARY }}-linux-musl
path: target/${{ env.MUSL_TARGET }}/release/${{ env.MCP_BINARY }}
retention-days: 3
- name: 上传 Release 产物 - name: 上传 Release 产物
if: needs.version.outputs.release_id != '' if: needs.version.outputs.release_id != ''
@@ -221,7 +231,7 @@ jobs:
run: | run: |
[ -z "$RELEASE_TOKEN" ] && exit 0 [ -z "$RELEASE_TOKEN" ] && exit 0
tag="${{ needs.version.outputs.tag }}" tag="${{ needs.version.outputs.tag }}"
bin="target/x86_64-unknown-linux-musl/release/${{ env.MCP_BINARY }}" bin="target/${{ env.MUSL_TARGET }}/release/${{ env.MCP_BINARY }}"
archive="${{ env.MCP_BINARY }}-${tag}-x86_64-linux-musl.tar.gz" archive="${{ env.MCP_BINARY }}-${tag}-x86_64-linux-musl.tar.gz"
tar -czf "$archive" -C "$(dirname "$bin")" "$(basename "$bin")" tar -czf "$archive" -C "$(dirname "$bin")" "$(basename "$bin")"
sha256sum "$archive" > "${archive}.sha256" sha256sum "$archive" > "${archive}.sha256"
@@ -231,70 +241,21 @@ jobs:
curl -fsS -H "Authorization: token $RELEASE_TOKEN" \ curl -fsS -H "Authorization: token $RELEASE_TOKEN" \
-F "attachment=@${archive}.sha256" "$release_url" -F "attachment=@${archive}.sha256" "$release_url"
- name: 飞书通知
if: always()
env:
WEBHOOK_URL: ${{ vars.WEBHOOK_URL }}
run: |
[ -z "$WEBHOOK_URL" ] && exit 0
command -v jq >/dev/null 2>&1 || (sudo apt-get update -qq && sudo apt-get install -y -qq jq)
tag="${{ needs.version.outputs.tag }}"
commit=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "N/A")
url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}"
result="${{ job.status }}"
if [ "$result" = "success" ]; then icon="✅"; else icon="❌"; fi
msg="secrets-mcp linux 构建${icon}
版本:${tag}
提交:${commit}
作者:${{ github.actor }}
详情:${url}"
payload=$(jq -n --arg text "$msg" '{msg_type: "text", content: {text: $text}}')
curl -sS -H "Content-Type: application/json" -X POST -d "$payload" "$WEBHOOK_URL"
deploy-mcp: deploy-mcp:
name: 部署 secrets-mcp name: 部署 secrets-mcp
needs: [version, build-linux] needs: [version, check, build-linux]
# 部署目标由仓库 Actions 配置vars.DEPLOY_HOST / vars.DEPLOY_USER私钥 secrets.DEPLOY_SSH_KEYPEM 原文,勿 base64 if: |
# (可用 scripts/setup-gitea-actions.sh 或 Gitea API 写入,勿写进本文件) needs.check.result == 'success' &&
# Google OAuth / SERVER_MASTER_KEY / SECRETS_DATABASE_URL 等勿写入 CI请在 ECS 上 needs.build-linux.result == 'success' &&
# /opt/secrets-mcp/.env 配置(见 deploy/.env.example (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/feat/mcp' || github.ref == 'refs/heads/mcp')
# 若仓库 main 仍为纯 CLI、仅 feat/mcp 含本 workflow请去掉条件里的 main避免误部署。
if: needs.build-linux.result == 'success' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/feat/mcp' || github.ref == 'refs/heads/mcp')
runs-on: debian runs-on: debian
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v4 - name: 下载构建产物
uses: actions/download-artifact@v3
- name: 安装 Rust
run: |
if ! command -v rustup >/dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain "${RUST_TOOLCHAIN}"
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
fi
source "$HOME/.cargo/env" 2>/dev/null || true
sudo apt-get update -qq && sudo apt-get install -y -qq pkg-config musl-tools
rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal
rustup default "${RUST_TOOLCHAIN}"
rustup target add x86_64-unknown-linux-musl --toolchain "${RUST_TOOLCHAIN}"
rustc -V
cargo -V
- name: 缓存 Cargo
uses: actions/cache@v4
with: with:
path: | name: ${{ env.MCP_BINARY }}-linux-musl
~/.cargo/registry/index path: /tmp/artifact
~/.cargo/registry/cache
~/.cargo/git/db
key: cargo-x86_64-unknown-linux-musl-${{ env.RUST_TOOLCHAIN }}-${{ hashFiles('Cargo.lock') }}
restore-keys: |
cargo-x86_64-unknown-linux-musl-${{ env.RUST_TOOLCHAIN }}-
cargo-x86_64-unknown-linux-musl-
- name: 构建 secrets-mcp
run: |
cargo build --release --locked --target x86_64-unknown-linux-musl -p secrets-mcp
strip target/x86_64-unknown-linux-musl/release/${{ env.MCP_BINARY }}
- name: 部署到阿里云 ECS - name: 部署到阿里云 ECS
env: env:
@@ -312,7 +273,7 @@ jobs:
SCP="scp -i /tmp/deploy_key -o StrictHostKeyChecking=no" SCP="scp -i /tmp/deploy_key -o StrictHostKeyChecking=no"
$SCP target/x86_64-unknown-linux-musl/release/${{ env.MCP_BINARY }} \ $SCP /tmp/artifact/${{ env.MCP_BINARY }} \
"${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/secrets-mcp.new" "${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/secrets-mcp.new"
ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no "${DEPLOY_USER}@${DEPLOY_HOST}" " ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no "${DEPLOY_USER}@${DEPLOY_HOST}" "
@@ -333,13 +294,11 @@ jobs:
[ -z "$WEBHOOK_URL" ] && exit 0 [ -z "$WEBHOOK_URL" ] && exit 0
command -v jq >/dev/null 2>&1 || (sudo apt-get update -qq && sudo apt-get install -y -qq jq) command -v jq >/dev/null 2>&1 || (sudo apt-get update -qq && sudo apt-get install -y -qq jq)
tag="${{ needs.version.outputs.tag }}" tag="${{ needs.version.outputs.tag }}"
commit=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "N/A")
url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}" url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}"
result="${{ job.status }}" result="${{ job.status }}"
if [ "$result" = "success" ]; then icon="✅"; else icon="❌"; fi if [ "$result" = "success" ]; then icon="✅"; else icon="❌"; fi
msg="secrets-mcp 部署${icon} msg="secrets-mcp 部署${icon}
版本:${tag} 版本:${tag}
提交:${commit}
作者:${{ github.actor }} 作者:${{ github.actor }}
详情:${url}" 详情:${url}"
payload=$(jq -n --arg text "$msg" '{msg_type: "text", content: {text: $text}}') payload=$(jq -n --arg text "$msg" '{msg_type: "text", content: {text: $text}}')
@@ -347,22 +306,21 @@ jobs:
publish-release: publish-release:
name: 发布草稿 Release name: 发布草稿 Release
needs: [version, build-linux] needs: [version, check, build-linux]
if: always() && needs.version.outputs.release_id != '' if: always() && needs.version.outputs.release_id != ''
runs-on: debian runs-on: debian
timeout-minutes: 5 timeout-minutes: 5
steps: steps:
- uses: actions/checkout@v4
- name: 发布草稿 - name: 发布草稿
env: env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: | run: |
[ -z "$RELEASE_TOKEN" ] && exit 0 [ -z "$RELEASE_TOKEN" ] && exit 0
check_r="${{ needs.check.result }}"
linux_r="${{ needs.build-linux.result }}" linux_r="${{ needs.build-linux.result }}"
if [ "$linux_r" != "success" ]; then if [ "$check_r" != "success" ] || [ "$linux_r" != "success" ]; then
echo "linux 构建未成功,保留草稿 Release" echo "质量检查或构建未成功check=${check_r}, build=${linux_r},保留草稿 Release"
exit 0 exit 0
fi fi
@@ -394,13 +352,16 @@ jobs:
[ -z "$commit" ] && commit="${{ github.sha }}" [ -z "$commit" ] && commit="${{ github.sha }}"
url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}" url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}"
check_r="${{ needs.check.result }}"
linux_r="${{ needs.build-linux.result }}" linux_r="${{ needs.build-linux.result }}"
publish_r="${{ job.status }}" publish_r="${{ job.status }}"
icon() { case "$1" in success) echo "✅";; skipped) echo "⏭";; *) echo "❌";; esac; } icon() { case "$1" in success) echo "✅";; skipped) echo "⏭";; *) echo "❌";; esac; }
if [ "$linux_r" = "success" ] && [ "$publish_r" = "success" ]; then if [ "$check_r" = "success" ] && [ "$linux_r" = "success" ] && [ "$publish_r" = "success" ]; then
status="发布成功 ✅" status="发布成功 ✅"
elif [ "$check_r" != "success" ]; then
status="检查失败 ❌"
elif [ "$linux_r" != "success" ]; then elif [ "$linux_r" != "success" ]; then
status="构建失败 ❌" status="构建失败 ❌"
else else
@@ -415,7 +376,7 @@ jobs:
msg="secrets-mcp ${status} msg="secrets-mcp ${status}
${version_line} ${version_line}
linux $(icon "$linux_r") | Release $(icon "$publish_r") check $(icon "$check_r") | linux $(icon "$linux_r") | Release $(icon "$publish_r")
提交:${commit} 提交:${commit}
作者:${{ github.actor }} 作者:${{ github.actor }}
详情:${url}" 详情:${url}"