refactor: workspace secrets-core + secrets-mcp MCP SaaS
- Split library (db/crypto/service) and MCP/Web/OAuth binary - Add deploy examples and CI/docs updates Made-with: Cursor
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
name: Secrets CLI - Build & Release
|
||||
# MCP 分支:仅构建/发布 secrets-mcp(CLI 在 main 分支维护)
|
||||
name: Secrets MCP — Build & Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [main, feat/mcp]
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'crates/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
# systemd / 部署模板变更也应跑构建(产物无变时可快速跳过 check)
|
||||
- 'deploy/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -16,8 +19,7 @@ permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
BINARY_NAME: secrets
|
||||
SECRETS_UPGRADE_URL: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/latest
|
||||
MCP_BINARY: secrets-mcp
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -40,9 +42,9 @@ jobs:
|
||||
- name: 解析版本
|
||||
id: ver
|
||||
run: |
|
||||
version=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
|
||||
tag="secrets-${version}"
|
||||
previous_tag=$(git tag --list 'secrets-*' --sort=-v:refname | awk -v tag="$tag" '$0 != tag { print; exit }')
|
||||
version=$(grep -m1 '^version' crates/secrets-mcp/Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
|
||||
tag="secrets-mcp-${version}"
|
||||
previous_tag=$(git tag --list 'secrets-mcp-*' --sort=-v:refname | awk -v tag="$tag" '$0 != tag { print; exit }')
|
||||
|
||||
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
|
||||
@@ -60,7 +62,7 @@ jobs:
|
||||
if: steps.ver.outputs.tag_exists == 'true'
|
||||
run: |
|
||||
echo "错误: 版本 ${{ steps.ver.outputs.tag }} 已存在,禁止重复发版。"
|
||||
echo "请先 bump Cargo.toml 中的 version,并执行 cargo build 同步 Cargo.lock。"
|
||||
echo "请先 bump crates/secrets-mcp/Cargo.toml 中的 version,并执行 cargo build 同步 Cargo.lock。"
|
||||
exit 1
|
||||
|
||||
- name: 创建 Tag
|
||||
@@ -112,7 +114,7 @@ jobs:
|
||||
|
||||
payload=$(jq -n \
|
||||
--arg tag "$tag" \
|
||||
--arg name "${{ env.BINARY_NAME }} ${version}" \
|
||||
--arg name "secrets-mcp ${version}" \
|
||||
--arg body "$body" \
|
||||
'{tag_name: $tag, name: $name, body: $body, draft: true}')
|
||||
|
||||
@@ -138,7 +140,7 @@ jobs:
|
||||
check:
|
||||
name: 质量检查 (fmt / clippy / test)
|
||||
runs-on: debian
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: 安装 Rust
|
||||
run: |
|
||||
@@ -168,10 +170,10 @@ jobs:
|
||||
- run: cargo test --locked
|
||||
|
||||
build-linux:
|
||||
name: Build (x86_64-unknown-linux-musl)
|
||||
name: Build Linux (secrets-mcp, musl)
|
||||
needs: [version, check]
|
||||
runs-on: debian
|
||||
timeout-minutes: 15
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: 安装依赖
|
||||
run: |
|
||||
@@ -198,8 +200,10 @@ jobs:
|
||||
restore-keys: |
|
||||
cargo-x86_64-unknown-linux-musl-
|
||||
|
||||
- run: cargo build --release --locked --target x86_64-unknown-linux-musl
|
||||
- run: strip target/x86_64-unknown-linux-musl/release/${{ env.BINARY_NAME }}
|
||||
- name: 构建 secrets-mcp (musl)
|
||||
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: 上传 Release 产物
|
||||
if: needs.version.outputs.release_id != ''
|
||||
@@ -208,16 +212,15 @@ jobs:
|
||||
run: |
|
||||
[ -z "$RELEASE_TOKEN" ] && exit 0
|
||||
tag="${{ needs.version.outputs.tag }}"
|
||||
bin="target/x86_64-unknown-linux-musl/release/${{ env.BINARY_NAME }}"
|
||||
archive="${{ env.BINARY_NAME }}-${tag}-x86_64-linux-musl.tar.gz"
|
||||
bin="target/x86_64-unknown-linux-musl/release/${{ env.MCP_BINARY }}"
|
||||
archive="${{ env.MCP_BINARY }}-${tag}-x86_64-linux-musl.tar.gz"
|
||||
tar -czf "$archive" -C "$(dirname "$bin")" "$(basename "$bin")"
|
||||
sha256sum "$archive" > "${archive}.sha256"
|
||||
release_url="${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${{ needs.version.outputs.release_id }}/assets"
|
||||
curl -fsS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@${archive}" \
|
||||
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${{ needs.version.outputs.release_id }}/assets"
|
||||
-F "attachment=@${archive}" "$release_url"
|
||||
curl -fsS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@${archive}.sha256" \
|
||||
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${{ needs.version.outputs.release_id }}/assets"
|
||||
-F "attachment=@${archive}.sha256" "$release_url"
|
||||
|
||||
- name: 飞书通知
|
||||
if: always()
|
||||
@@ -231,7 +234,7 @@ jobs:
|
||||
url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}"
|
||||
result="${{ job.status }}"
|
||||
if [ "$result" = "success" ]; then icon="✅"; else icon="❌"; fi
|
||||
msg="secrets linux 构建${icon}
|
||||
msg="secrets-mcp linux 构建${icon}
|
||||
版本:${tag}
|
||||
提交:${commit}
|
||||
作者:${{ github.actor }}
|
||||
@@ -239,23 +242,29 @@ jobs:
|
||||
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"
|
||||
|
||||
build-macos:
|
||||
name: Build (macOS aarch64 + x86_64)
|
||||
needs: [version, check]
|
||||
runs-on: darwin-arm64
|
||||
timeout-minutes: 15
|
||||
deploy-mcp:
|
||||
name: 部署 secrets-mcp
|
||||
needs: [version, build-linux]
|
||||
# 部署目标由仓库 Actions 配置:vars.DEPLOY_HOST / vars.DEPLOY_USER;私钥 secrets.DEPLOY_SSH_KEY(PEM 原文,勿 base64)
|
||||
# (可用 scripts/setup-gitea-actions.sh 或 Gitea API 写入,勿写进本文件)
|
||||
# Google OAuth / SERVER_MASTER_KEY / SECRETS_DATABASE_URL 等勿写入 CI,请在 ECS 上
|
||||
# /opt/secrets-mcp/.env 配置(见 deploy/.env.example)。
|
||||
# 若仓库 main 仍为纯 CLI、仅 feat/mcp 含本 workflow,请去掉条件里的 main,避免误部署。
|
||||
if: needs.build-linux.result == 'success' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/feat/mcp')
|
||||
runs-on: debian
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: 安装依赖
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 安装 Rust
|
||||
run: |
|
||||
if ! command -v cargo >/dev/null 2>&1; then
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
fi
|
||||
source "$HOME/.cargo/env" 2>/dev/null || true
|
||||
rustup target add aarch64-apple-darwin
|
||||
rustup target add x86_64-apple-darwin
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
sudo apt-get update -qq && sudo apt-get install -y -qq pkg-config musl-tools
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
|
||||
- name: 缓存 Cargo
|
||||
uses: actions/cache@v4
|
||||
@@ -265,45 +274,43 @@ jobs:
|
||||
~/.cargo/registry/cache
|
||||
~/.cargo/git/db
|
||||
target
|
||||
key: cargo-macos-${{ hashFiles('Cargo.lock') }}
|
||||
key: cargo-x86_64-unknown-linux-musl-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
cargo-macos-
|
||||
cargo-x86_64-unknown-linux-musl-
|
||||
|
||||
- run: cargo build --release --locked --target aarch64-apple-darwin
|
||||
- run: cargo build --release --locked --target x86_64-apple-darwin
|
||||
- run: strip -x target/aarch64-apple-darwin/release/${{ env.BINARY_NAME }}
|
||||
- run: strip -x target/x86_64-apple-darwin/release/${{ env.BINARY_NAME }}
|
||||
|
||||
- name: 上传 Release 产物
|
||||
if: needs.version.outputs.release_id != ''
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
- name: 构建 secrets-mcp
|
||||
run: |
|
||||
[ -z "$RELEASE_TOKEN" ] && exit 0
|
||||
tag="${{ needs.version.outputs.tag }}"
|
||||
release_id="${{ needs.version.outputs.release_id }}"
|
||||
cargo build --release --locked --target x86_64-unknown-linux-musl -p secrets-mcp
|
||||
strip target/x86_64-unknown-linux-musl/release/${{ env.MCP_BINARY }}
|
||||
|
||||
arm_bin="target/aarch64-apple-darwin/release/${{ env.BINARY_NAME }}"
|
||||
arm_archive="${{ env.BINARY_NAME }}-${tag}-aarch64-macos.tar.gz"
|
||||
tar -czf "$arm_archive" -C "$(dirname "$arm_bin")" "$(basename "$arm_bin")"
|
||||
shasum -a 256 "$arm_archive" > "${arm_archive}.sha256"
|
||||
curl -fsS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@${arm_archive}" \
|
||||
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${release_id}/assets"
|
||||
curl -fsS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@${arm_archive}.sha256" \
|
||||
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${release_id}/assets"
|
||||
- name: 部署到阿里云 ECS
|
||||
env:
|
||||
DEPLOY_HOST: ${{ vars.DEPLOY_HOST }}
|
||||
DEPLOY_USER: ${{ vars.DEPLOY_USER }}
|
||||
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
run: |
|
||||
if [ -z "$DEPLOY_HOST" ] || [ -z "$DEPLOY_USER" ] || [ -z "$DEPLOY_SSH_KEY" ]; then
|
||||
echo "部署跳过:请在仓库 Actions 中配置 vars.DEPLOY_HOST、vars.DEPLOY_USER 与 secrets.DEPLOY_SSH_KEY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
intel_bin="target/x86_64-apple-darwin/release/${{ env.BINARY_NAME }}"
|
||||
intel_archive="${{ env.BINARY_NAME }}-${tag}-x86_64-macos.tar.gz"
|
||||
tar -czf "$intel_archive" -C "$(dirname "$intel_bin")" "$(basename "$intel_bin")"
|
||||
shasum -a 256 "$intel_archive" > "${intel_archive}.sha256"
|
||||
curl -fsS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@${intel_archive}" \
|
||||
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${release_id}/assets"
|
||||
curl -fsS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@${intel_archive}.sha256" \
|
||||
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${release_id}/assets"
|
||||
echo "$DEPLOY_SSH_KEY" > /tmp/deploy_key
|
||||
chmod 600 /tmp/deploy_key
|
||||
|
||||
SCP="scp -i /tmp/deploy_key -o StrictHostKeyChecking=no"
|
||||
|
||||
$SCP target/x86_64-unknown-linux-musl/release/${{ env.MCP_BINARY }} \
|
||||
"${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/secrets-mcp.new"
|
||||
|
||||
ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no "${DEPLOY_USER}@${DEPLOY_HOST}" "
|
||||
sudo mv /tmp/secrets-mcp.new /opt/secrets-mcp/secrets-mcp
|
||||
sudo chmod +x /opt/secrets-mcp/secrets-mcp
|
||||
sudo systemctl restart secrets-mcp
|
||||
sleep 2
|
||||
sudo systemctl is-active secrets-mcp && echo '服务启动成功' || (sudo journalctl -u secrets-mcp -n 20 && exit 1)
|
||||
"
|
||||
|
||||
rm -f /tmp/deploy_key
|
||||
|
||||
- name: 飞书通知
|
||||
if: always()
|
||||
@@ -311,102 +318,29 @@ jobs:
|
||||
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 macOS 双架构构建${icon}
|
||||
msg="secrets-mcp 部署${icon}
|
||||
版本:${tag}
|
||||
目标:aarch64-apple-darwin, x86_64-apple-darwin
|
||||
提交:${commit}
|
||||
作者:${{ github.actor }}
|
||||
详情:${url}"
|
||||
payload=$(python3 -c "import json,sys; print(json.dumps({'msg_type':'text','content':{'text':sys.argv[1]}}))" "$msg")
|
||||
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"
|
||||
|
||||
build-windows:
|
||||
name: Build (x86_64-pc-windows-msvc)
|
||||
needs: [version, check]
|
||||
runs-on: windows
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: 安装依赖
|
||||
shell: pwsh
|
||||
run: |
|
||||
$cargoBin = Join-Path $env:USERPROFILE ".cargo\bin"
|
||||
if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
|
||||
Invoke-WebRequest -Uri "https://win.rustup.rs/x86_64" -OutFile rustup-init.exe
|
||||
.\rustup-init.exe -y --default-toolchain stable
|
||||
Remove-Item rustup-init.exe
|
||||
}
|
||||
$env:Path = "$cargoBin;$env:Path"
|
||||
Add-Content -Path $env:GITHUB_PATH -Value $cargoBin
|
||||
rustup target add x86_64-pc-windows-msvc
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 缓存 Cargo
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
~/.cargo/git/db
|
||||
target
|
||||
key: cargo-x86_64-pc-windows-msvc-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
cargo-x86_64-pc-windows-msvc-
|
||||
|
||||
- name: 构建
|
||||
shell: pwsh
|
||||
run: cargo build --release --locked --target x86_64-pc-windows-msvc
|
||||
|
||||
- name: 上传 Release 产物
|
||||
if: needs.version.outputs.release_id != ''
|
||||
shell: pwsh
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
run: |
|
||||
if (-not $env:RELEASE_TOKEN) { exit 0 }
|
||||
$tag = "${{ needs.version.outputs.tag }}"
|
||||
$bin = "target\x86_64-pc-windows-msvc\release\${{ env.BINARY_NAME }}.exe"
|
||||
$archive = "${{ env.BINARY_NAME }}-${tag}-x86_64-windows.zip"
|
||||
Compress-Archive -Path $bin -DestinationPath $archive -Force
|
||||
$hash = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLower()
|
||||
Set-Content -Path "${archive}.sha256" -Value "$hash $archive" -NoNewline
|
||||
$url = "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${{ needs.version.outputs.release_id }}/assets"
|
||||
Invoke-RestMethod -Uri $url -Method Post `
|
||||
-Headers @{ "Authorization" = "token $env:RELEASE_TOKEN" } `
|
||||
-Form @{ attachment = Get-Item $archive }
|
||||
Invoke-RestMethod -Uri $url -Method Post `
|
||||
-Headers @{ "Authorization" = "token $env:RELEASE_TOKEN" } `
|
||||
-Form @{ attachment = Get-Item "${archive}.sha256" }
|
||||
|
||||
- name: 飞书通知
|
||||
if: always()
|
||||
shell: pwsh
|
||||
env:
|
||||
WEBHOOK_URL: ${{ vars.WEBHOOK_URL }}
|
||||
run: |
|
||||
if (-not $env:WEBHOOK_URL) { exit 0 }
|
||||
$tag = "${{ needs.version.outputs.tag }}"
|
||||
$commit = (git log -1 --pretty=format:"%s" 2>$null) ?? "N/A"
|
||||
$url = "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}"
|
||||
$result = "${{ job.status }}"
|
||||
$icon = if ($result -eq "success") { "✅" } else { "❌" }
|
||||
$msg = "secrets windows 构建${icon}`n版本:${tag}`n提交:${commit}`n作者:${{ github.actor }}`n详情:${url}"
|
||||
$payload = @{ msg_type = "text"; content = @{ text = $msg } } | ConvertTo-Json
|
||||
Invoke-RestMethod -Uri $env:WEBHOOK_URL -Method Post `
|
||||
-ContentType "application/json" -Body $payload
|
||||
|
||||
publish-release:
|
||||
name: 发布草稿 Release
|
||||
needs: [version, build-linux, build-macos, build-windows]
|
||||
needs: [version, build-linux]
|
||||
if: always() && needs.version.outputs.release_id != ''
|
||||
runs-on: debian
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 发布草稿
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
@@ -414,11 +348,8 @@ jobs:
|
||||
[ -z "$RELEASE_TOKEN" ] && exit 0
|
||||
|
||||
linux_r="${{ needs.build-linux.result }}"
|
||||
macos_r="${{ needs.build-macos.result }}"
|
||||
windows_r="${{ needs.build-windows.result }}"
|
||||
if [ "$linux_r" != "success" ] || [ "$macos_r" != "success" ] || [ "$windows_r" != "success" ]; then
|
||||
echo "存在未成功的构建任务,保留草稿 Release"
|
||||
echo "linux=${linux_r} macos=${macos_r} windows=${windows_r}"
|
||||
if [ "$linux_r" != "success" ]; then
|
||||
echo "linux 构建未成功,保留草稿 Release"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -451,15 +382,13 @@ jobs:
|
||||
url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}"
|
||||
|
||||
linux_r="${{ needs.build-linux.result }}"
|
||||
macos_r="${{ needs.build-macos.result }}"
|
||||
windows_r="${{ needs.build-windows.result }}"
|
||||
publish_r="${{ job.status }}"
|
||||
|
||||
icon() { case "$1" in success) echo "✅";; skipped) echo "⏭";; *) echo "❌";; esac; }
|
||||
|
||||
if [ "$linux_r" = "success" ] && [ "$macos_r" = "success" ] && [ "$windows_r" = "success" ] && [ "$publish_r" = "success" ]; then
|
||||
if [ "$linux_r" = "success" ] && [ "$publish_r" = "success" ]; then
|
||||
status="发布成功 ✅"
|
||||
elif [ "$linux_r" != "success" ] || [ "$macos_r" != "success" ] || [ "$windows_r" != "success" ]; then
|
||||
elif [ "$linux_r" != "success" ]; then
|
||||
status="构建失败 ❌"
|
||||
else
|
||||
status="发布失败 ❌"
|
||||
@@ -471,9 +400,9 @@ jobs:
|
||||
version_line="🔄 重复构建 ${tag}"
|
||||
fi
|
||||
|
||||
msg="secrets ${status}
|
||||
msg="secrets-mcp ${status}
|
||||
${version_line}
|
||||
linux $(icon "$linux_r") | macOS $(icon "$macos_r") | windows $(icon "$windows_r") | Release $(icon "$publish_r")
|
||||
linux $(icon "$linux_r") | Release $(icon "$publish_r")
|
||||
提交:${commit}
|
||||
作者:${{ github.actor }}
|
||||
详情:${url}"
|
||||
|
||||
Reference in New Issue
Block a user