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: # ========== 版本检查 + Release 预创建(单次运行,消除多 job 竞态)========== version: name: 版本 & Release runs-on: debian outputs: version: ${{ steps.ver.outputs.version }} tag: ${{ steps.ver.outputs.tag }} tag_exists: ${{ steps.ver.outputs.tag_exists }} release_id: ${{ steps.release.outputs.release_id }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: 解析版本 id: ver 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.ver.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.ver.outputs.tag }}" -m "Release ${{ steps.ver.outputs.tag }}" git push origin "${{ steps.ver.outputs.tag }}" - name: 预创建 Release id: release if: steps.ver.outputs.tag_exists == 'false' env: RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | if [ -z "$RELEASE_TOKEN" ]; then echo "release_id=" >> $GITHUB_OUTPUT exit 0 fi command -v jq &>/dev/null || (sudo apt-get update -qq && sudo apt-get install -y -qq jq) tag="${{ steps.ver.outputs.tag }}" url="${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases" release_id=$(curl -sS -H "Authorization: token $RELEASE_TOKEN" \ -H "Content-Type: application/json" -X POST "$url" \ -d "{\"tag_name\":\"${tag}\",\"name\":\"${tag}\",\"body\":\"Release ${tag}\"}" \ | jq -r '.id // empty') echo "release_id=${release_id}" >> $GITHUB_OUTPUT # ========== 代码质量检查(只在 Linux 跑一次)========== check: name: 质量检查 (fmt / clippy / test) runs-on: debian timeout-minutes: 1 # 超时1分钟 steps: - name: 安装 Rust run: | if ! command -v cargo &>/dev/null; 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 component add rustfmt clippy - uses: actions/checkout@v4 - name: 缓存 Cargo uses: actions/cache@v4 with: path: | ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db target key: cargo-check-${{ hashFiles('Cargo.lock') }} restore-keys: cargo-check- - run: cargo fmt -- --check - run: cargo clippy --locked -- -D warnings - run: cargo test --locked # ========== 多平台构建 ========== build: name: Build (${{ matrix.target }}) needs: [version, check] continue-on-error: true timeout-minutes: 10 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 pkg-config musl-tools binutils curl if ! command -v cargo &>/dev/null; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable fi source "$HOME/.cargo/env" 2>/dev/null || true rustup target add ${{ matrix.target }} echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: 安装依赖 (macOS) if: matrix.os == 'macos' run: | if ! command -v cargo &>/dev/null; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable fi source "$HOME/.cargo/env" 2>/dev/null || true rustup target add ${{ matrix.target }} echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: 安装依赖 (Windows) if: matrix.os == 'windows' shell: pwsh run: | 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 } rustup target add ${{ matrix.target }} - uses: actions/checkout@v4 - name: 缓存 Cargo uses: actions/cache@v4 with: path: | ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db target key: cargo-${{ matrix.target }}-${{ hashFiles('Cargo.lock') }} restore-keys: cargo-${{ matrix.target }}- - name: 构建 if: matrix.os != 'windows' run: cargo build --release --locked --target ${{ matrix.target }} - name: 构建 (Windows) if: matrix.os == 'windows' shell: pwsh run: cargo build --release --locked --target ${{ matrix.target }} - 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 }} - name: 上传产物 (Linux/macOS) if: needs.version.outputs.tag_exists == 'false' && needs.version.outputs.release_id && matrix.os != 'windows' env: RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | [ -z "$RELEASE_TOKEN" ] && exit 0 tag="${{ needs.version.outputs.tag }}" bin="target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}" archive="${{ env.BINARY_NAME }}-${tag}-${{ matrix.archive_suffix }}.tar.gz" tar -czf "$archive" -C "$(dirname $bin)" "$(basename $bin)" curl -sS -H "Authorization: token $RELEASE_TOKEN" \ -F "attachment=@${archive}" \ "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${{ needs.version.outputs.release_id }}/assets" echo "已上传: ${archive}" - name: 上传产物 (Windows) if: needs.version.outputs.tag_exists == 'false' && needs.version.outputs.release_id && matrix.os == 'windows' shell: pwsh env: RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | if (-not $env:RELEASE_TOKEN) { exit 0 } $tag = "${{ needs.version.outputs.tag }}" $bin = "target\${{ matrix.target }}\release\${{ env.BINARY_NAME }}.exe" $archive = "${{ env.BINARY_NAME }}-${tag}-${{ matrix.archive_suffix }}.zip" Compress-Archive -Path $bin -DestinationPath $archive $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 } Write-Host "已上传: ${archive}" # ========== 通知 ========== notify: name: 通知 needs: [version] if: always() && github.event_name == 'push' runs-on: debian timeout-minutes: 1 continue-on-error: true steps: - uses: actions/checkout@v4 - name: 发送飞书通知 env: WEBHOOK_URL: ${{ vars.WEBHOOK_URL }} run: | [ -z "$WEBHOOK_URL" ] && exit 0 command -v jq &>/dev/null || (sudo apt-get update -qq && sudo apt-get install -y -qq jq) tag="${{ needs.version.outputs.tag }}" tag_exists="${{ needs.version.outputs.tag_exists }}" commit=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "N/A") url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}" msg="${{ env.BINARY_NAME }} 通知" if [ "$tag_exists" = "false" ]; then msg="${msg} 🆕 已创建版本标签并开始构建发布" else msg="${msg} 🔄 检测到已有版本标签,已开始重复构建" fi msg="${msg} 提交:${commit} 版本:${tag} 作者:${{ 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"