feat: secrets CLI MVP — add/search/delete with PostgreSQL JSONB
Some checks failed
Secrets CLI - Build & Release / 检查版本 (push) Successful in 2s
Secrets CLI - Build & Release / Build (x86_64-unknown-linux-musl) (push) Failing after 41s
Secrets CLI - Build & Release / Build (aarch64-apple-darwin) (push) Failing after 55s
Secrets CLI - Build & Release / 发送通知 (push) Has been cancelled
Secrets CLI - Build & Release / Build (x86_64-pc-windows-msvc) (push) Has been cancelled
Some checks failed
Secrets CLI - Build & Release / 检查版本 (push) Successful in 2s
Secrets CLI - Build & Release / Build (x86_64-unknown-linux-musl) (push) Failing after 41s
Secrets CLI - Build & Release / Build (aarch64-apple-darwin) (push) Failing after 55s
Secrets CLI - Build & Release / 发送通知 (push) Has been cancelled
Secrets CLI - Build & Release / Build (x86_64-pc-windows-msvc) (push) Has been cancelled
- Single `secrets` table with namespace/kind/name/tags/metadata/encrypted - Auto-migrate on startup using uuidv7() primary keys and GIN indexes - CLI commands: add (upsert, @file support), search (full-text + tags), delete - Multi-platform Gitea Actions: debian (x86_64-musl), darwin-arm64, windows - continue-on-error + timeout-minutes=30 for offline runner tolerance - VS Code tasks.json for local build/test/seed - AGENTS.md for AI context Made-with: Cursor
This commit is contained in:
313
.gitea/workflows/secrets.yml
Normal file
313
.gitea/workflows/secrets.yml
Normal file
@@ -0,0 +1,313 @@
|
||||
name: Secrets CLI - Build & Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- '.gitea/workflows/secrets.yml'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
BINARY_NAME: secrets
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_BACKTRACE: short
|
||||
|
||||
jobs:
|
||||
# ========== 版本检查(只跑一次,供后续 job 共用)==========
|
||||
version:
|
||||
name: 检查版本
|
||||
runs-on: debian
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
tag: ${{ steps.version.outputs.tag }}
|
||||
tag_exists: ${{ steps.version.outputs.tag_exists }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 检查版本
|
||||
id: version
|
||||
run: |
|
||||
version=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
|
||||
tag="secrets-${version}"
|
||||
echo "version=${version}" >> $GITHUB_OUTPUT
|
||||
echo "tag=${tag}" >> $GITHUB_OUTPUT
|
||||
|
||||
if git rev-parse "refs/tags/${tag}" >/dev/null 2>&1; then
|
||||
echo "tag_exists=true" >> $GITHUB_OUTPUT
|
||||
echo "版本 ${tag} 已存在"
|
||||
else
|
||||
echo "tag_exists=false" >> $GITHUB_OUTPUT
|
||||
echo "将创建新版本 ${tag}"
|
||||
fi
|
||||
|
||||
- name: 创建 Tag
|
||||
if: steps.version.outputs.tag_exists == 'false'
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.tag }}"
|
||||
git push origin "${{ steps.version.outputs.tag }}"
|
||||
|
||||
# ========== 矩阵构建 ==========
|
||||
build:
|
||||
name: Build (${{ matrix.target }})
|
||||
needs: version
|
||||
continue-on-error: true # 某平台失败/超时不阻断其他平台和 notify
|
||||
timeout-minutes: 30 # runner 不在线时 30 分钟后放弃
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: debian
|
||||
target: x86_64-unknown-linux-musl
|
||||
archive_suffix: x86_64-linux-musl
|
||||
os: linux
|
||||
- runner: darwin-arm64
|
||||
target: aarch64-apple-darwin
|
||||
archive_suffix: aarch64-macos
|
||||
os: macos
|
||||
- runner: windows
|
||||
target: x86_64-pc-windows-msvc
|
||||
archive_suffix: x86_64-windows
|
||||
os: windows
|
||||
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
# ========== 环境准备 ==========
|
||||
- name: 安装依赖 (Linux)
|
||||
if: matrix.os == 'linux'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y jq curl git pkg-config musl-tools binutils
|
||||
|
||||
if ! command -v cargo &>/dev/null; then
|
||||
echo "安装 Rust 工具链..."
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
||||
source "$HOME/.cargo/env"
|
||||
rustup component add rustfmt clippy
|
||||
fi
|
||||
|
||||
rustup target add ${{ matrix.target }}
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: 安装依赖 (macOS)
|
||||
if: matrix.os == 'macos'
|
||||
run: |
|
||||
brew install jq
|
||||
|
||||
if ! command -v cargo &>/dev/null; then
|
||||
echo "安装 Rust 工具链..."
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
||||
source "$HOME/.cargo/env"
|
||||
rustup component add rustfmt clippy
|
||||
fi
|
||||
|
||||
rustup target add ${{ matrix.target }}
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: 安装依赖 (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
# 检查 Rust 是否已安装
|
||||
if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "安装 Rust 工具链..."
|
||||
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
|
||||
}
|
||||
rustup component add rustfmt clippy
|
||||
rustup target add ${{ matrix.target }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# ========== Cargo 缓存 ==========
|
||||
- name: 缓存 Cargo 依赖
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
~/.cargo/git/db
|
||||
target
|
||||
key: cargo-secrets-${{ matrix.target }}-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
cargo-secrets-${{ matrix.target }}-
|
||||
|
||||
- name: 检查代码格式
|
||||
if: matrix.os != 'windows'
|
||||
run: cargo fmt -- --check
|
||||
|
||||
- name: 检查代码格式 (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: cargo fmt -- --check
|
||||
|
||||
- name: 运行 Clippy 检查
|
||||
if: matrix.os != 'windows'
|
||||
run: cargo clippy --release --target ${{ matrix.target }} -- -D warnings
|
||||
|
||||
- name: 运行 Clippy 检查 (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: cargo clippy --release --target ${{ matrix.target }} -- -D warnings
|
||||
|
||||
- name: 构建
|
||||
if: matrix.os != 'windows'
|
||||
env:
|
||||
GIT_TAG: ${{ needs.version.outputs.version }}
|
||||
run: cargo build --release --target ${{ matrix.target }} --verbose
|
||||
|
||||
- name: 构建 (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
env:
|
||||
GIT_TAG: ${{ needs.version.outputs.version }}
|
||||
run: cargo build --release --target ${{ matrix.target }} --verbose
|
||||
|
||||
- name: Strip 二进制 (Linux)
|
||||
if: matrix.os == 'linux'
|
||||
run: strip target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}
|
||||
|
||||
- name: Strip 二进制 (macOS)
|
||||
if: matrix.os == 'macos'
|
||||
run: strip -x target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}
|
||||
|
||||
# ========== 上传 Release 产物 ==========
|
||||
- name: 上传 Release 产物 (Linux/macOS)
|
||||
if: needs.version.outputs.tag_exists == 'false' && matrix.os != 'windows'
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
run: |
|
||||
[ -z "$RELEASE_TOKEN" ] && echo "跳过:未配置 RELEASE_TOKEN" && exit 0
|
||||
|
||||
tag="${{ needs.version.outputs.tag }}"
|
||||
binary="target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}"
|
||||
archive="${{ env.BINARY_NAME }}-${tag}-${{ matrix.archive_suffix }}.tar.gz"
|
||||
|
||||
tar -czf "$archive" -C "$(dirname $binary)" "$(basename $binary)"
|
||||
|
||||
# 查找已有 Release(由首个完成的 job 创建,后续 job 直接上传)
|
||||
release_url="${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases"
|
||||
release_id=$(curl -sS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
"${release_url}/tags/${tag}" | jq -r '.id // empty')
|
||||
|
||||
if [ -z "$release_id" ]; then
|
||||
release_id=$(curl -sS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST "$release_url" \
|
||||
-d "{\"tag_name\": \"${tag}\", \"name\": \"${tag}\", \"body\": \"Release ${tag}\"}" \
|
||||
| jq -r '.id')
|
||||
fi
|
||||
|
||||
upload_url="${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${release_id}/assets"
|
||||
curl -sS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@${archive}" \
|
||||
"$upload_url"
|
||||
|
||||
echo "已上传: ${archive} → Release ${tag}"
|
||||
|
||||
- name: 上传 Release 产物 (Windows)
|
||||
if: needs.version.outputs.tag_exists == 'false' && matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
run: |
|
||||
if (-not $env:RELEASE_TOKEN) { Write-Host "跳过:未配置 RELEASE_TOKEN"; exit 0 }
|
||||
|
||||
$tag = "${{ needs.version.outputs.tag }}"
|
||||
$binary = "target\${{ matrix.target }}\release\${{ env.BINARY_NAME }}.exe"
|
||||
$archive = "${{ env.BINARY_NAME }}-${tag}-${{ matrix.archive_suffix }}.zip"
|
||||
|
||||
Compress-Archive -Path $binary -DestinationPath $archive
|
||||
|
||||
$headers = @{ "Authorization" = "token $env:RELEASE_TOKEN"; "Content-Type" = "application/json" }
|
||||
$release_url = "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases"
|
||||
|
||||
# 查找已有 Release
|
||||
$existing = Invoke-RestMethod -Uri "${release_url}/tags/${tag}" -Headers $headers -ErrorAction SilentlyContinue
|
||||
if ($existing.id) {
|
||||
$release_id = $existing.id
|
||||
} else {
|
||||
$body = @{ tag_name = $tag; name = $tag; body = "Release ${tag}" } | ConvertTo-Json
|
||||
$release_id = (Invoke-RestMethod -Uri $release_url -Method Post -Headers $headers -Body $body).id
|
||||
}
|
||||
|
||||
$upload_url = "${release_url}/${release_id}/assets"
|
||||
$upload_headers = @{ "Authorization" = "token $env:RELEASE_TOKEN" }
|
||||
$form = @{ attachment = Get-Item $archive }
|
||||
Invoke-RestMethod -Uri $upload_url -Method Post -Headers $upload_headers -Form $form
|
||||
|
||||
Write-Host "已上传: ${archive} → Release ${tag}"
|
||||
|
||||
# ========== 汇总通知 ==========
|
||||
notify:
|
||||
name: 发送通知
|
||||
needs: [version, build]
|
||||
if: always() && github.event_name == 'push'
|
||||
runs-on: debian
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 发送通知
|
||||
continue-on-error: true
|
||||
env:
|
||||
WEBHOOK_URL: ${{ vars.WEBHOOK_URL }}
|
||||
run: |
|
||||
[ -z "$WEBHOOK_URL" ] && exit 0
|
||||
|
||||
tag="${{ needs.version.outputs.tag }}"
|
||||
tag_exists="${{ needs.version.outputs.tag_exists }}"
|
||||
build_result="${{ needs.build.result }}"
|
||||
|
||||
if [ "$build_result" = "success" ]; then
|
||||
status_text="构建成功 ✅"
|
||||
else
|
||||
status_text="构建失败 ❌"
|
||||
fi
|
||||
|
||||
commit_title=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "N/A")
|
||||
workflow_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}"
|
||||
|
||||
if [ "$build_result" != "success" ]; then
|
||||
payload=$(jq -n \
|
||||
--arg title "${{ env.BINARY_NAME }} ${status_text}" \
|
||||
--arg commit "$commit_title" \
|
||||
--arg version "$tag" \
|
||||
--arg author "${{ github.actor }}" \
|
||||
--arg url "$workflow_url" \
|
||||
'{msg_type: "text", content: {text: "\($title)\n提交:\($commit)\n版本:\($version)\n作者:\($author)\n详情:\($url)"}}')
|
||||
elif [ "$tag_exists" = "false" ]; then
|
||||
payload=$(jq -n \
|
||||
--arg title "${{ env.BINARY_NAME }} ${status_text}" \
|
||||
--arg commit "$commit_title" \
|
||||
--arg version "$tag" \
|
||||
--arg author "${{ github.actor }}" \
|
||||
--arg url "$workflow_url" \
|
||||
'{msg_type: "text", content: {text: "\($title)\n🆕 新版本已发布 (linux / macOS / windows)\n提交:\($commit)\n版本:\($version)\n作者:\($author)\n详情:\($url)"}}')
|
||||
else
|
||||
payload=$(jq -n \
|
||||
--arg title "${{ env.BINARY_NAME }} ${status_text}" \
|
||||
--arg commit "$commit_title" \
|
||||
--arg version "$tag" \
|
||||
--arg author "${{ github.actor }}" \
|
||||
--arg url "$workflow_url" \
|
||||
'{msg_type: "text", content: {text: "\($title)\n🔄 重复构建\n提交:\($commit)\n版本:\($version)\n作者:\($author)\n详情:\($url)"}}')
|
||||
fi
|
||||
|
||||
curl -sS -H "Content-Type: application/json" -X POST -d "$payload" "$WEBHOOK_URL"
|
||||
Reference in New Issue
Block a user