feat: add Gitee install script & verbose upload logging #83
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build & Release | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: write | |
| packages: write | |
| jobs: | |
| # โโ ๅคๆญๆฏๅฆ้่ฆๆๅปบ / ๅๅธ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| check: | |
| name: Check commit message | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should_build: ${{ steps.flags.outputs.should_build }} | |
| should_release: ${{ steps.flags.outputs.should_release }} | |
| should_publish: ${{ steps.flags.outputs.should_publish }} | |
| should_publish_pypi: ${{ steps.flags.outputs.should_publish_pypi }} | |
| should_publish_crates: ${{ steps.flags.outputs.should_publish_crates }} | |
| should_benchmark: ${{ steps.flags.outputs.should_benchmark }} | |
| version: ${{ steps.flags.outputs.version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Parse commit message | |
| id: flags | |
| run: | | |
| MSG="${{ github.event.head_commit.message }}" | |
| # ไป Cargo.toml ๆๅ็ๆฌๅท | |
| VERSION="v$(grep '^version' rust/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "๐ฆ Version: $VERSION" | |
| # PR ๅง็ปๆๅปบ | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| echo "should_build=true" >> "$GITHUB_OUTPUT" | |
| echo "should_release=false" >> "$GITHUB_OUTPUT" | |
| echo "should_publish=false" >> "$GITHUB_OUTPUT" | |
| echo "should_benchmark=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # ๅๅงๅๆ ๅฟ | |
| BUILD=false | |
| RELEASE=false | |
| PUBLISH=false | |
| PUBLISH_PYPI=false | |
| PUBLISH_CRATES=false | |
| BENCHMARK=false | |
| # "build publish" = ๆๅปบ + Release + ๅๅธๅฐๅ ็ฎก็ๅจ (ๅ จๅฅ) | |
| if echo "$MSG" | grep -qi "build publish"; then | |
| BUILD=true | |
| RELEASE=true | |
| PUBLISH=true | |
| # "build release" = ๆๅปบ + GitHub Release | |
| elif echo "$MSG" | grep -qi "build release"; then | |
| BUILD=true | |
| RELEASE=true | |
| # "build action" = ไป ๆๅปบ | |
| elif echo "$MSG" | grep -qi "build action"; then | |
| BUILD=true | |
| fi | |
| # "publish from release" = ไปๅทฒๆ Release ๅๅธๅฐๅ ็ฎก็ๅจ (ไธๆๅปบ) | |
| if echo "$MSG" | grep -qi "publish from release"; then | |
| PUBLISH=true | |
| fi | |
| # "pypi publish" / "publish pypi" = ๅๅธๅฐ PyPI | |
| if echo "$MSG" | grep -qiE "pypi publish|publish pypi"; then | |
| PUBLISH_PYPI=true | |
| fi | |
| # "crates publish" / "publish crates" = ๅๅธๅฐ crates.io | |
| if echo "$MSG" | grep -qiE "crates publish|publish crates"; then | |
| PUBLISH_CRATES=true | |
| fi | |
| # "run benchmark" = ่ฟ่กๅบๅๆต่ฏ | |
| if echo "$MSG" | grep -qi "run benchmark"; then | |
| BENCHMARK=true | |
| fi | |
| echo "should_build=$BUILD" >> "$GITHUB_OUTPUT" | |
| echo "should_release=$RELEASE" >> "$GITHUB_OUTPUT" | |
| echo "should_publish=$PUBLISH" >> "$GITHUB_OUTPUT" | |
| echo "should_publish_pypi=$PUBLISH_PYPI" >> "$GITHUB_OUTPUT" | |
| echo "should_publish_crates=$PUBLISH_CRATES" >> "$GITHUB_OUTPUT" | |
| echo "should_benchmark=$BENCHMARK" >> "$GITHUB_OUTPUT" | |
| echo "๐ Flags: build=$BUILD release=$RELEASE publish=$PUBLISH publish_pypi=$PUBLISH_PYPI publish_crates=$PUBLISH_CRATES benchmark=$BENCHMARK" | |
| # โโ ๅๆญฅไปฃ็ ๅฐ Gitee โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๆฏๆฌก push ้ฝๅๆญฅ๏ผไธ check ๅนถ่ก่ฟ่ก | |
| sync-gitee-code: | |
| name: ใGiteeใSync Code | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' | |
| steps: | |
| - name: Mirror to Gitee | |
| uses: Yikun/hub-mirror-action@v1.5 | |
| with: | |
| src: github/VincentZyuApps | |
| dst: gitee/vincent-zyu | |
| dst_key: ${{ secrets.GITEE_PRIVATE_KEY }} | |
| dst_token: ${{ secrets.GITEE_TOKEN }} | |
| static_list: "winload" | |
| force_update: true | |
| debug: true | |
| # โโ Benchmark โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| benchmark: | |
| name: Run Benchmark | |
| needs: check | |
| if: needs.check.outputs.should_benchmark == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: sudo apt-get update && sudo apt-get install -y time | |
| - name: Run benchmark | |
| run: | | |
| chmod +x benchmark_go/benchmark.sh | |
| cd benchmark_go | |
| ./benchmark.sh | |
| - name: Commit and push benchmark results | |
| run: | | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| git add docs/benchmark/benchmark.svg | |
| git commit -m "docs: update benchmark results [skip ci]" || echo "No changes to commit" | |
| git push origin main | |
| # โโ ๅคๅนณๅฐๆๅปบ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| build: | |
| name: Build ${{ matrix.name }} | |
| needs: check | |
| if: needs.check.outputs.should_build == 'true' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # โโ Windows x64 (MSVC, native) โโ | |
| - target: x86_64-pc-windows-msvc | |
| os: windows-latest | |
| name: windows-x86_64 | |
| binary: winload.exe | |
| asset: winload-windows-x86_64.exe | |
| # โโ Linux x64 (musl ้ๆ้พๆฅ, ๅ ผๅฎนๆๆ Linux) โโ | |
| - target: x86_64-unknown-linux-musl | |
| os: ubuntu-latest | |
| name: linux-x86_64 | |
| binary: winload | |
| asset: winload-linux-x86_64 | |
| cross_packages: musl-tools | |
| # โโ Windows ARM64 (cross-compile on x64 runner) โโ | |
| - target: aarch64-pc-windows-msvc | |
| os: windows-latest | |
| name: windows-aarch64 | |
| binary: winload.exe | |
| asset: winload-windows-aarch64.exe | |
| # โโ Linux ARM64 (ubuntu-22.04 ้ไฝ GLIBC ่ฆๆฑ) โโ | |
| - target: aarch64-unknown-linux-gnu | |
| os: ubuntu-22.04 | |
| name: linux-aarch64 | |
| binary: winload | |
| asset: winload-linux-aarch64 | |
| cross_linker: aarch64-linux-gnu-gcc | |
| cross_packages: gcc-aarch64-linux-gnu | |
| # โโ macOS x64 (build on Apple Silicon runner) โโ | |
| - target: x86_64-apple-darwin | |
| os: macos-latest | |
| name: macos-x86_64 | |
| binary: winload | |
| asset: winload-macos-x86_64 | |
| # โโ macOS ARM64 (Apple Silicon runner) โโ | |
| - target: aarch64-apple-darwin | |
| os: macos-latest | |
| name: macos-aarch64 | |
| binary: winload | |
| asset: winload-macos-aarch64 | |
| # โโ Android aarch64 (Termux ARM64) โโ | |
| - target: aarch64-linux-android | |
| os: ubuntu-latest | |
| name: android-aarch64 | |
| binary: winload | |
| asset: winload-android-aarch64 | |
| # โโ Android x86_64 (Termux x86_64, ๆจกๆๅจ / Chromebook) โโ | |
| - target: x86_64-linux-android | |
| os: ubuntu-latest | |
| name: android-x86_64 | |
| binary: winload | |
| asset: winload-android-x86_64 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: rust -> target | |
| cache-on-failure: true | |
| key: ${{ matrix.target }} | |
| - name: Install platform toolchain | |
| if: matrix.cross_packages | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y ${{ matrix.cross_packages }} | |
| # Android: ้ ็ฝฎ NDK ไบคๅ็ผ่ฏๅทฅๅ ท้พ (Termux ๆไฝ API 24 = Android 7.0) | |
| - name: Setup Android NDK | |
| if: contains(matrix.target, 'android') | |
| shell: bash | |
| run: | | |
| NDK_HOME="${ANDROID_NDK_LATEST_HOME:-$ANDROID_NDK_HOME}" | |
| echo "๐ฆ Using NDK: $NDK_HOME" | |
| TOOLCHAIN="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin" | |
| ls "$TOOLCHAIN" | head -20 | |
| if [[ "${{ matrix.target }}" == "aarch64-linux-android" ]]; then | |
| echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=${TOOLCHAIN}/aarch64-linux-android24-clang" >> "$GITHUB_ENV" | |
| echo "CC_aarch64_linux_android=${TOOLCHAIN}/aarch64-linux-android24-clang" >> "$GITHUB_ENV" | |
| echo "AR_aarch64_linux_android=${TOOLCHAIN}/llvm-ar" >> "$GITHUB_ENV" | |
| elif [[ "${{ matrix.target }}" == "x86_64-linux-android" ]]; then | |
| echo "CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=${TOOLCHAIN}/x86_64-linux-android24-clang" >> "$GITHUB_ENV" | |
| echo "CC_x86_64_linux_android=${TOOLCHAIN}/x86_64-linux-android24-clang" >> "$GITHUB_ENV" | |
| echo "AR_x86_64_linux_android=${TOOLCHAIN}/llvm-ar" >> "$GITHUB_ENV" | |
| fi | |
| echo "โ Android NDK configured for ${{ matrix.target }}" | |
| # Windows: ไธ่ฝฝ Npcap SDK๏ผpcap crate ้พๆฅ wpcap.lib ้่ฆ | |
| - name: Install Npcap SDK (Windows only) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $sdkUrl = "https://npcap.com/dist/npcap-sdk-1.13.zip" | |
| $zipPath = "$env:RUNNER_TEMP\npcap-sdk.zip" | |
| $extractPath = "$env:RUNNER_TEMP\npcap-sdk" | |
| Write-Host "๐ฅ Downloading Npcap SDK..." | |
| Invoke-WebRequest -Uri $sdkUrl -OutFile $zipPath | |
| Write-Host "๐ฆ Extracting Npcap SDK..." | |
| Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force | |
| # ๆ นๆฎ target ้ๆฉๆญฃ็กฎ็ lib ็ฎๅฝ | |
| $target = "${{ matrix.target }}" | |
| if ($target -like "*aarch64*") { | |
| $arch = "ARM64" | |
| } else { | |
| $arch = "x64" | |
| } | |
| $libDir = "$extractPath\Lib\$arch" | |
| Write-Host "๐ Npcap SDK lib dir ($arch): $libDir" | |
| Get-ChildItem $libDir | |
| echo "LIB=$libDir;$env:LIB" >> $env:GITHUB_ENV | |
| Write-Host "โ Npcap SDK ready" | |
| - name: Install Linux packaging tools | |
| if: runner.os == 'Linux' && !contains(matrix.target, 'android') | |
| run: | | |
| cargo install cargo-deb cargo-generate-rpm | |
| - name: Build release binary | |
| shell: bash | |
| working-directory: rust | |
| env: | |
| # ไบคๅ็ผ่ฏๆถๆๅฎ้พๆฅๅจ (ไป aarch64-linux-gnu ้่ฆ) | |
| CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.cross_linker || '' }} | |
| run: | | |
| cargo build --release --target ${{ matrix.target }} | |
| # ้ช่ฏ Linux musl ไบง็ฉๆฏๅฆ็็ๆฏ้ๆ้พๆฅ | |
| if [[ "${{ matrix.target }}" == *musl* ]]; then | |
| echo "๐ Verifying static linking..." | |
| file target/${{ matrix.target }}/release/${{ matrix.binary }} | |
| ldd target/${{ matrix.target }}/release/${{ matrix.binary }} 2>&1 || echo "โ Statically linked (no dynamic dependencies)" | |
| fi | |
| # Linux targets: ้ขๅคๆๅปบ DEB + RPM ๅ (ๆ้ค Android) | |
| - name: Build DEB & RPM packages | |
| if: contains(matrix.target, 'linux') && !contains(matrix.target, 'android') | |
| shell: bash | |
| working-directory: rust | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| echo "๐ฆ Building DEB package for ${{ matrix.target }}..." | |
| cargo deb --target ${{ matrix.target }} --no-build | |
| echo "๐ฆ Building RPM package for ${{ matrix.target }}..." | |
| cargo generate-rpm --target ${{ matrix.target }} | |
| echo "โ Packages built (original names):" | |
| find target -name '*.deb' -o -name '*.rpm' | head -20 | |
| # ้ๅฝๅไธบ็ปไธๆ ผๅผ: winload-linux-<arch>-<version>.deb / .rpm | |
| mkdir -p renamed | |
| for f in $(find target -name '*.deb'); do | |
| cp "$f" "renamed/winload-${{ matrix.name }}-${VERSION}.deb" | |
| done | |
| for f in $(find target -name '*.rpm'); do | |
| cp "$f" "renamed/winload-${{ matrix.name }}-${VERSION}.rpm" | |
| done | |
| echo "๐ฆ Renamed packages:" | |
| ls -lh renamed/ | |
| - name: Upload DEB artifact | |
| if: contains(matrix.target, 'linux') && !contains(matrix.target, 'android') | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: winload-${{ matrix.name }}-${{ needs.check.outputs.version }}.deb | |
| path: rust/renamed/*.deb | |
| - name: Upload RPM artifact | |
| if: contains(matrix.target, 'linux') && !contains(matrix.target, 'android') | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: winload-${{ matrix.name }}-${{ needs.check.outputs.version }}.rpm | |
| path: rust/renamed/*.rpm | |
| - name: Prepare artifact | |
| shell: bash | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| ASSET_BASE="${{ matrix.asset }}" | |
| # ๅจๆฉๅฑๅๅๆๅ ฅ็ๆฌๅท | |
| # winload-linux-x86_64 -> winload-linux-x86_64-v0.1.0 | |
| # winload-windows-x86_64.exe -> winload-windows-x86_64-v0.1.0.exe | |
| if [[ "$ASSET_BASE" == *.* ]]; then | |
| # ๆๆฉๅฑๅ | |
| BASE_NAME="${ASSET_BASE%.*}" | |
| EXTENSION="${ASSET_BASE##*.}" | |
| ASSET_WITH_VERSION="${BASE_NAME}-${VERSION}.${EXTENSION}" | |
| else | |
| # ๆ ๆฉๅฑๅ | |
| ASSET_WITH_VERSION="${ASSET_BASE}-${VERSION}" | |
| fi | |
| echo "๐ฆ Output: $ASSET_WITH_VERSION" | |
| cp "rust/target/${{ matrix.target }}/release/${{ matrix.binary }}" "$ASSET_WITH_VERSION" | |
| echo "asset_name=$ASSET_WITH_VERSION" >> "$GITHUB_ENV" | |
| - name: Upload build artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ env.asset_name }} | |
| path: ${{ env.asset_name }} | |
| # โโ ๅๅธๅฐ GitHub Releases โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| release: | |
| name: Publish Release | |
| needs: [check, build] | |
| if: needs.check.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| merge-multiple: true | |
| - name: List downloaded files | |
| run: | | |
| echo "Downloaded artifacts:" | |
| ls -lh | |
| - name: Delete existing release & tag (force fresh release) | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| echo "๐๏ธ Cleaning up existing release/tag: $VERSION" | |
| gh release delete "$VERSION" --yes --cleanup-tag 2>/dev/null || echo "No existing release to delete" | |
| # ็ญๅพ GitHub API ๅๆญฅ | |
| sleep 3 | |
| - name: Generate release notes from commits | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| REPO="${{ github.repository }}" | |
| BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}" | |
| # ๆฅๆพไธไธไธช release tag๏ผๆ้คๅฝๅ็ๆฌ๏ผ | |
| PREV_TAG=$(git tag --sort=-v:refname | grep -v "^${VERSION}$" | head -1) | |
| { | |
| echo "## ๐ฆ winload ${VERSION}" | |
| echo "" | |
| echo "### โฌ๏ธ Downloads" | |
| echo "" | |
| echo "| Arch | Windows | Linux | macOS |" | |
| echo "|------|---------|-------|-------|" | |
| echo "| **x86_64** | [exe](${BASE_URL}/winload-windows-x86_64-${VERSION}.exe) | [binary](${BASE_URL}/winload-linux-x86_64-${VERSION}) ยท [deb](${BASE_URL}/winload-linux-x86_64-${VERSION}.deb) ยท [rpm](${BASE_URL}/winload-linux-x86_64-${VERSION}.rpm) | [binary](${BASE_URL}/winload-macos-x86_64-${VERSION}) |" | |
| echo "| **ARM64** | [exe](${BASE_URL}/winload-windows-aarch64-${VERSION}.exe) | [binary](${BASE_URL}/winload-linux-aarch64-${VERSION}) ยท [deb](${BASE_URL}/winload-linux-aarch64-${VERSION}.deb) ยท [rpm](${BASE_URL}/winload-linux-aarch64-${VERSION}.rpm) | [binary](${BASE_URL}/winload-macos-aarch64-${VERSION}) |" | |
| echo "" | |
| # ๆๅไธๅธฆ v ๅ็ผ็็ๆฌๅท | |
| PLAIN_VER="${VERSION#v}" | |
| # PyPI ็ๆฌๅท: PEP 440 ่ง่ๅ (0.1.7-rc.10 -> 0.1.7rc10) | |
| PYPI_VER=$(echo "$PLAIN_VER" | sed 's/-rc\./rc/') | |
| echo "### ๐ฅ Quick Install" | |
| echo "" | |
| echo "**Python (pip):**" | |
| echo '```bash' | |
| echo "pip install winload==${PYPI_VER}" | |
| echo "# or with uv" | |
| echo "uv pip install winload==${PYPI_VER}" | |
| echo '```' | |
| echo "> ๐ [PyPI](https://pypi.org/project/winload/${PYPI_VER}/)" | |
| echo "" | |
| echo "**npm (cross-platform):**" | |
| echo '```bash' | |
| echo "npm install -g @vincentzyuapps/winload@${PLAIN_VER}" | |
| echo '```' | |
| echo "> ๐ [npm](https://www.npmjs.com/package/@vincentzyuapps/winload/v/${PLAIN_VER})" | |
| echo "" | |
| echo "**Cargo (build from source):**" | |
| echo '```bash' | |
| echo "cargo install winload@${PLAIN_VER}" | |
| echo '```' | |
| echo "> ๐ [crates.io](https://crates.io/crates/winload/${PLAIN_VER})" | |
| echo "" | |
| echo "**Windows (Scoop):**" | |
| echo '```powershell' | |
| echo "scoop bucket add vincentzyu https://github.com/VincentZyuApps/scoop-bucket" | |
| echo "scoop install winload@${PLAIN_VER}" | |
| echo '```' | |
| echo "> ๐ [Scoop bucket](https://github.com/VincentZyuApps/scoop-bucket)" | |
| echo "" | |
| echo "**Arch Linux (AUR):**" | |
| echo '```bash' | |
| echo "paru -S winload-rust-bin" | |
| echo '```' | |
| echo "> ๐ [winload-rust-bin](https://aur.archlinux.org/packages/winload-rust-bin)" | |
| echo "" | |
| echo "**One-liner install for some Linux distros (Debian/Ubuntu/RHEL/Fedora and derivatives):**" | |
| echo '```bash' | |
| echo "curl -fsSL https://raw.githubusercontent.com/${REPO}/main/docs/install_scripts/install.sh | bash" | |
| echo "# or install this specific version:" | |
| echo "WINLOAD_VERSION=${VERSION} bash -c \"\\\$(curl -fsSL https://raw.githubusercontent.com/${REPO}/main/docs/install_scripts/install.sh)\"" | |
| echo '```' | |
| echo "> ๐ [View install script source](https://github.com/${REPO}/blob/main/docs/install_scripts/install.sh)" | |
| echo "" | |
| echo "**๐จ๐ณ Gitee mirror (faster in China):**" | |
| echo '```bash' | |
| echo "curl -fsSL https://gitee.com/vincent-zyu/winload/raw/main/docs/install_scripts/install_gitee.sh | bash" | |
| echo '```' | |
| echo "> ๐ [View Gitee install script](https://gitee.com/vincent-zyu/winload/blob/main/docs/install_scripts/install_gitee.sh)" | |
| echo "" | |
| echo "> โ ๏ธ These install scripts only support systems with **apt or dnf** on **x86_64 / aarch64**. For other platforms, use **npm** or **Cargo**." | |
| echo "> " | |
| echo "> โ ๏ธ ไปฅไธๅฎ่ฃ ่ๆฌไป ้็จไบไฝฟ็จ **apt ๆ dnf** ็ **x86_64 / aarch64** ็ณป็ปใๅ ถไปๅนณๅฐ่ฏทไฝฟ็จ **npm** ๆ **Cargo** ๅฎ่ฃ ใ" | |
| echo "" | |
| echo "---" | |
| echo "" | |
| echo "### What's Changed" | |
| echo "" | |
| if [ -n "$PREV_TAG" ]; then | |
| echo "Changes since **${PREV_TAG}**:" | |
| echo "" | |
| git log "${PREV_TAG}..HEAD" --pretty=format:"- %s (\`%h\`) โ @%an" --reverse | |
| else | |
| echo "All changes since repository creation:" | |
| echo "" | |
| git log --pretty=format:"- %s (\`%h\`) โ @%an" --reverse | |
| fi | |
| echo "" | |
| echo "" | |
| echo "---" | |
| echo "" | |
| echo "### ๐ Build Info" | |
| echo "- **Build date**: $(date -u '+%Y-%m-%d %H:%M UTC')" | |
| echo "- **Commit**: [\`${GITHUB_SHA::7}\`](https://github.com/${REPO}/commit/${GITHUB_SHA})" | |
| echo "" | |
| if [ -n "$PREV_TAG" ]; then | |
| echo "**Full Changelog**: https://github.com/${REPO}/compare/${PREV_TAG}...${VERSION}" | |
| else | |
| echo "**Full Changelog**: https://github.com/${REPO}/commits/${VERSION}" | |
| fi | |
| } > release_notes.md | |
| echo "๐ Generated release notes:" | |
| cat release_notes.md | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| # ๆถ้ๆๆ่ฆไธไผ ็ๆไปถ๏ผไบ่ฟๅถ + DEB + RPM๏ผ็ปไธ winload-*-VERSION* ๆ ผๅผ๏ผ | |
| shopt -s nullglob | |
| FILES=(winload-*-${VERSION}*) | |
| echo "๐ค Uploading ${#FILES[@]} files:" | |
| printf ' - %s\n' "${FILES[@]}" | |
| gh release create "$VERSION" \ | |
| --title "winload ${VERSION}" \ | |
| --notes-file release_notes.md \ | |
| --latest \ | |
| "${FILES[@]}" | |
| # โโ ๆดๆฐ Scoop Bucket โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ่งฆๅๆนๅผ: | |
| # - "publish from release" โ ไปๅทฒๆ Release ๆๅไบ่ฟๅถ๏ผไป ๆดๆฐ Scoop | |
| # - "build publish" โ ๆๅปบ + Release + ๆดๆฐ Scoop๏ผๅ จๅฅ๏ผ | |
| publish-scoop: | |
| name: Update Scoop Bucket | |
| needs: [check, release] | |
| # always() ่ฎฉๆญค job ๅณไฝฟ release ่ขซ่ทณ่ฟไน่ฝ่ฟ่ก | |
| if: always() && needs.check.outputs.should_publish == 'true' && (needs.release.result == 'success' || needs.release.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Fetch latest release info | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| REPO="${{ github.repository }}" | |
| BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}" | |
| echo "๐ฆ Target version: ${VERSION}" | |
| echo "๐ฅ Downloading Windows binaries from release..." | |
| # ไธ่ฝฝ Windows x64 | |
| curl -fSL -o winload-windows-x86_64.exe \ | |
| "${BASE_URL}/winload-windows-x86_64-${VERSION}.exe" \ | |
| || { echo "โ Failed to download x64 binary"; exit 1; } | |
| # ไธ่ฝฝ Windows ARM64 | |
| curl -fSL -o winload-windows-aarch64.exe \ | |
| "${BASE_URL}/winload-windows-aarch64-${VERSION}.exe" \ | |
| || { echo "โ Failed to download arm64 binary"; exit 1; } | |
| echo "โ Downloaded:" | |
| ls -lh winload-windows-*.exe | |
| - name: Generate Scoop manifest | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| SCOOP_VERSION="${VERSION#v}" | |
| REPO="${{ github.repository }}" | |
| BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}" | |
| # ่ฎก็ฎ sha256 | |
| HASH_X64=$(sha256sum winload-windows-x86_64.exe | awk '{print $1}') | |
| HASH_ARM64=$(sha256sum winload-windows-aarch64.exe | awk '{print $1}') | |
| echo "๐ฆ Version: $SCOOP_VERSION" | |
| echo "๐ x64 hash: $HASH_X64" | |
| echo "๐ arm64 hash: $HASH_ARM64" | |
| cat > winload.json << EOF | |
| { | |
| "version": "${SCOOP_VERSION}", | |
| "description": "Network Load Monitor - nload for Windows/Linux/macOS", | |
| "homepage": "https://github.com/${REPO}", | |
| "license": "MIT", | |
| "architecture": { | |
| "64bit": { | |
| "url": "${BASE_URL}/winload-windows-x86_64-${VERSION}.exe", | |
| "hash": "${HASH_X64}", | |
| "bin": [["winload-windows-x86_64-${VERSION}.exe", "win-nload"]] | |
| }, | |
| "arm64": { | |
| "url": "${BASE_URL}/winload-windows-aarch64-${VERSION}.exe", | |
| "hash": "${HASH_ARM64}", | |
| "bin": [["winload-windows-aarch64-${VERSION}.exe", "win-nload"]] | |
| } | |
| }, | |
| "checkver": { | |
| "github": "https://github.com/${REPO}" | |
| }, | |
| "autoupdate": { | |
| "architecture": { | |
| "64bit": { | |
| "url": "https://github.com/${REPO}/releases/download/v\$version/winload-windows-x86_64-v\$version.exe" | |
| }, | |
| "arm64": { | |
| "url": "https://github.com/${REPO}/releases/download/v\$version/winload-windows-aarch64-v\$version.exe" | |
| } | |
| } | |
| } | |
| } | |
| EOF | |
| echo "๐ Generated winload.json:" | |
| cat winload.json | |
| - name: Push to scoop-bucket repo | |
| env: | |
| SCOOP_TOKEN: ${{ secrets.SCOOP_BUCKET_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| git clone https://x-access-token:${SCOOP_TOKEN}@github.com/VincentZyuApps/scoop-bucket.git scoop-repo | |
| cp winload.json scoop-repo/bucket/winload.json | |
| cd scoop-repo | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add bucket/winload.json | |
| git commit -m "๐บ Update winload to ${VERSION}" || echo "No changes to commit" | |
| git push | |
| echo "โ Scoop bucket updated!" | |
| # โโ ๆดๆฐ AUR ๅ ๏ผwinload-rust-bin: ้ข็ผ่ฏไบ่ฟๅถ๏ผโโโโโโโโโโโโโ | |
| publish-aur-bin: | |
| name: Update AUR Package (winload-rust-bin) | |
| needs: [check, release] | |
| if: always() && needs.check.outputs.should_publish == 'true' && (needs.release.result == 'success' || needs.release.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Download Linux binaries from release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| REPO="${{ github.repository }}" | |
| BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}" | |
| echo "๐ฅ Downloading x86_64 binary..." | |
| curl -fSL -o winload-linux-x86_64 "${BASE_URL}/winload-linux-x86_64-${VERSION}" | |
| SHA256_X86=$(sha256sum winload-linux-x86_64 | awk '{print $1}') | |
| echo "๐ x86_64 SHA256: $SHA256_X86" | |
| echo "SHA256_X86=$SHA256_X86" >> "$GITHUB_ENV" | |
| echo "๐ฅ Downloading aarch64 binary..." | |
| curl -fSL -o winload-linux-aarch64 "${BASE_URL}/winload-linux-aarch64-${VERSION}" | |
| SHA256_ARM=$(sha256sum winload-linux-aarch64 | awk '{print $1}') | |
| echo "๐ aarch64 SHA256: $SHA256_ARM" | |
| echo "SHA256_ARM=$SHA256_ARM" >> "$GITHUB_ENV" | |
| - name: Generate PKGBUILD | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| TAGVER="${VERSION#v}" # 0.1.6-beta.3 (for download URL) | |
| PKGVER="${TAGVER//-/.}" # 0.1.6.beta.3 (AUR compliant) | |
| cat > PKGBUILD << HEREDOC_END | |
| # Maintainer: VincentZyu <vincentzyu233@gmail.com> | |
| pkgname=winload-rust-bin | |
| pkgver=${PKGVER} | |
| pkgrel=1 | |
| pkgdesc="A lightweight, real-time CLI tool for monitoring network bandwidth and traffic" | |
| arch=('x86_64' 'aarch64') | |
| url="https://github.com/VincentZyuApps/winload" | |
| license=('MIT') | |
| provides=('winload') | |
| conflicts=('winload' 'winload-rust') | |
| _tagver=${TAGVER} | |
| _base_url="https://github.com/VincentZyuApps/winload/releases/download/v\${_tagver}" | |
| source_x86_64=("winload-linux-x86_64-v\${_tagver}::\${_base_url}/winload-linux-x86_64-v\${_tagver}") | |
| source_aarch64=("winload-linux-aarch64-v\${_tagver}::\${_base_url}/winload-linux-aarch64-v\${_tagver}") | |
| noextract=() | |
| sha256sums_x86_64=('${SHA256_X86}') | |
| sha256sums_aarch64=('${SHA256_ARM}') | |
| package() { | |
| if [[ "\$CARCH" == "x86_64" ]]; then | |
| install -Dm755 "\$srcdir/winload-linux-x86_64-v\${_tagver}" "\$pkgdir/usr/bin/winload" | |
| elif [[ "\$CARCH" == "aarch64" ]]; then | |
| install -Dm755 "\$srcdir/winload-linux-aarch64-v\${_tagver}" "\$pkgdir/usr/bin/winload" | |
| fi | |
| } | |
| HEREDOC_END | |
| # Remove leading whitespace (heredoc indentation) | |
| sed -i 's/^ //' PKGBUILD | |
| echo "๐ Generated PKGBUILD:" | |
| cat PKGBUILD | |
| - name: Generate .SRCINFO | |
| uses: docker://archlinux:latest | |
| with: | |
| args: bash -c "pacman -Sy --noconfirm pacman-contrib base-devel && useradd -m builder && cp PKGBUILD /home/builder/ && cd /home/builder && sudo -u builder makepkg --printsrcinfo > .SRCINFO && cp .SRCINFO /github/workspace/" | |
| - name: Push to AUR | |
| env: | |
| AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| TAGVER="${VERSION#v}" # 0.1.6-beta.3 (for download URL) | |
| PKGVER="${TAGVER//-/.}" # 0.1.6.beta.3 (AUR compliant) | |
| # Setup SSH for AUR | |
| mkdir -p ~/.ssh | |
| echo "$AUR_SSH_KEY" > ~/.ssh/aur | |
| chmod 600 ~/.ssh/aur | |
| cat >> ~/.ssh/config << 'SSHEOF' | |
| Host aur.archlinux.org | |
| IdentityFile ~/.ssh/aur | |
| User aur | |
| StrictHostKeyChecking no | |
| SSHEOF | |
| # Clone AUR repo | |
| git clone ssh://aur@aur.archlinux.org/winload-rust-bin.git aur-repo || { | |
| echo "โ ๏ธ AUR repo doesn't exist yet. Please create it manually first." | |
| echo " Run: ssh aur@aur.archlinux.org setup-repo winload-rust-bin" | |
| exit 1 | |
| } | |
| # Copy files | |
| cp PKGBUILD aur-repo/ | |
| cp .SRCINFO aur-repo/ | |
| # Commit and push | |
| cd aur-repo | |
| git config user.name "VincentZyu" | |
| git config user.email "vincentzyu233@gmail.com" | |
| git add PKGBUILD .SRCINFO | |
| git commit -m "Update to ${PKGVER}" || echo "No changes to commit" | |
| git push | |
| echo "โ winload-rust-bin updated!" | |
| # โโ ๅๅธๅฐ npm๏ผ@vincentzyuapps/winload: ้ข็ผ่ฏไบ่ฟๅถ๏ผโโโโโโโโโโโโ | |
| # ๅ็ๅ esbuild / @biomejs/biome / turbo: | |
| # 1. ไธบๆฏไธชๅนณๅฐๅๅธไธไธชๅชๅซไบ่ฟๅถ็ๅ (os/cpu ๅญๆฎต่ฎฉ npm ่ชๅจ็ญ้) | |
| # 2. ไธปๅ @vincentzyuapps/winload ้่ฟ optionalDependencies ๅผ็จๅฎไปฌ | |
| # 3. ็จๆท npm install ๆถๅชไธ่ฝฝๅน้ ๅฝๅๅนณๅฐ็้ฃไธไธช | |
| # 4. ๅๆถๅๅธๅฐ GitHub Packages (npm.pkg.github.com) | |
| publish-npm: | |
| name: Publish to npm | |
| needs: [check, release] | |
| if: always() && needs.check.outputs.should_publish == 'true' && (needs.release.result == 'success' || needs.release.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Download binaries from release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| REPO="${{ github.repository }}" | |
| BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}" | |
| mkdir -p artifacts | |
| echo "๐ฅ Downloading binaries for npm publish..." | |
| curl -fSL -o artifacts/winload-windows-x86_64.exe "${BASE_URL}/winload-windows-x86_64-${VERSION}.exe" | |
| curl -fSL -o artifacts/winload-windows-aarch64.exe "${BASE_URL}/winload-windows-aarch64-${VERSION}.exe" | |
| curl -fSL -o artifacts/winload-linux-x86_64 "${BASE_URL}/winload-linux-x86_64-${VERSION}" | |
| curl -fSL -o artifacts/winload-linux-aarch64 "${BASE_URL}/winload-linux-aarch64-${VERSION}" | |
| curl -fSL -o artifacts/winload-macos-x86_64 "${BASE_URL}/winload-macos-x86_64-${VERSION}" | |
| curl -fSL -o artifacts/winload-macos-aarch64 "${BASE_URL}/winload-macos-aarch64-${VERSION}" | |
| echo "โ Downloaded:" | |
| ls -lh artifacts/ | |
| - name: Publish platform packages | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| NPM_VERSION="${VERSION#v}" | |
| NPM_TAG="latest" | |
| echo "๐ฆ npm version: ${NPM_VERSION} (tag: ${NPM_TAG})" | |
| # ๅนณๅฐๅฎไน: npmๅ ๅ|os|cpu|ๆบๆไปถ|ไบ่ฟๅถๅ | |
| PLATFORMS=( | |
| "@vincentzyuapps/winload-win32-x64|win32|x64|artifacts/winload-windows-x86_64.exe|winload.exe" | |
| "@vincentzyuapps/winload-win32-arm64|win32|arm64|artifacts/winload-windows-aarch64.exe|winload.exe" | |
| "@vincentzyuapps/winload-linux-x64|linux|x64|artifacts/winload-linux-x86_64|winload" | |
| "@vincentzyuapps/winload-linux-arm64|linux|arm64|artifacts/winload-linux-aarch64|winload" | |
| "@vincentzyuapps/winload-darwin-x64|darwin|x64|artifacts/winload-macos-x86_64|winload" | |
| "@vincentzyuapps/winload-darwin-arm64|darwin|arm64|artifacts/winload-macos-aarch64|winload" | |
| ) | |
| for entry in "${PLATFORMS[@]}"; do | |
| IFS='|' read -r PKG_NAME PKG_OS PKG_CPU SOURCE_BIN BIN_NAME <<< "$entry" | |
| echo "" | |
| echo "๐ฆ Publishing ${PKG_NAME}@${NPM_VERSION}..." | |
| PKG_DIR="npm-platforms/${PKG_NAME}" | |
| mkdir -p "${PKG_DIR}/bin" | |
| cp "${SOURCE_BIN}" "${PKG_DIR}/bin/${BIN_NAME}" | |
| chmod +x "${PKG_DIR}/bin/${BIN_NAME}" | |
| cat > "${PKG_DIR}/package.json" << PKGJSON | |
| { | |
| "name": "${PKG_NAME}", | |
| "version": "${NPM_VERSION}", | |
| "description": "winload binary for ${PKG_OS}-${PKG_CPU}", | |
| "license": "MIT", | |
| "repository": { | |
| "type": "git", | |
| "url": "https://github.com/${{ github.repository }}" | |
| }, | |
| "os": ["${PKG_OS}"], | |
| "cpu": ["${PKG_CPU}"], | |
| "files": ["bin/"] | |
| } | |
| PKGJSON | |
| cd "${PKG_DIR}" | |
| npm publish --access public --tag "${NPM_TAG}" 2>&1 || echo "โ ๏ธ Failed to publish ${PKG_NAME} (may already exist)" | |
| cd "$GITHUB_WORKSPACE" | |
| done | |
| - name: Publish main package | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| NPM_VERSION="${VERSION#v}" | |
| NPM_TAG="latest" | |
| # ๅคๅถ้กน็ฎๆ น็ฎๅฝ็ readme.md ไฝไธบ npm ้กต้ขๅฑ็คบ | |
| cp readme.md npm/winload-rust-bin/readme.md | |
| cd npm/winload-rust-bin | |
| # ๅจๆๆดๆฐ package.json ไธญ็็ๆฌๅท๏ผ็ๆฌไปฅ Cargo.toml ไธบๅฏไธ็ๅฎๆบ๏ผ | |
| node -e " | |
| const fs = require('fs'); | |
| const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); | |
| pkg.version = '${NPM_VERSION}'; | |
| for (const dep of Object.keys(pkg.optionalDependencies || {})) { | |
| pkg.optionalDependencies[dep] = '${NPM_VERSION}'; | |
| } | |
| fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n'); | |
| " | |
| echo "๐ฆ Publishing @vincentzyuapps/winload@${NPM_VERSION} (tag: ${NPM_TAG})..." | |
| cat package.json | |
| npm publish --access public --tag "${NPM_TAG}" | |
| echo "โ npm packages published!" | |
| # โโ ๅๆญฅๅๅธๅฐ GitHub Packages โโ | |
| # ็ดๆฅๅจๆฏไธชๅ ็ฎๅฝๅๅ ฅ .npmrc๏ผ็กฎไฟ npm ่ฝๆพๅฐ npm.pkg.github.com ็่ฎค่ฏใ | |
| # (actions/setup-node ็ๆ็ .npmrc ๅชๅ ๅซ registry.npmjs.org ็่ฎค่ฏ) | |
| - name: Publish platform packages to GitHub Packages | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| NPM_VERSION="${VERSION#v}" | |
| NPM_TAG="latest" | |
| for dir in npm-platforms/@vincentzyuapps/*/; do | |
| [ -d "$dir" ] || continue | |
| echo "๐ฆ Publishing $(basename $dir) to GitHub Packages..." | |
| cd "$dir" | |
| echo "//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}" > .npmrc | |
| npm publish --registry https://npm.pkg.github.com --tag "${NPM_TAG}" 2>&1 || echo "โ ๏ธ Failed to publish $(basename $dir) to GitHub Packages (may already exist)" | |
| cd "$GITHUB_WORKSPACE" | |
| done | |
| - name: Publish main package to GitHub Packages | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| cd npm/winload-rust-bin | |
| echo "//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}" > .npmrc | |
| npm publish --registry https://npm.pkg.github.com --tag "latest" | |
| echo "โ GitHub Packages published!" | |
| # โโ ๅๅธๅฐ PyPI (Python ๅ ) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| publish-pypi: | |
| name: Publish to PyPI | |
| needs: check | |
| if: needs.check.outputs.should_publish_pypi == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v4 | |
| - name: Copy readme for PyPI | |
| run: cp readme.md py/readme.md | |
| - name: Build package | |
| working-directory: py | |
| run: | | |
| uv build | |
| - name: Publish to PyPI | |
| working-directory: py | |
| env: | |
| UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} | |
| run: | | |
| uv publish --publish-url https://upload.pypi.org/legacy/ | |
| # โโ ๅๅธๅฐ crates.io (Rust ๅ ) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| publish-crates-io: | |
| name: Publish to crates.io | |
| needs: [check, build] | |
| if: needs.check.outputs.should_publish_crates == 'true' && needs.build.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Prepare README for crates.io | |
| run: | | |
| pwd | |
| ls -la | |
| cp ./readme.md ./rust/readme.md | |
| - name: Publish to crates.io | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| run: | | |
| cd rust | |
| cargo publish --allow-dirty --token "$CARGO_REGISTRY_TOKEN" | |
| echo "โ Published to crates.io!" | |
| # โโ ๅๆญฅ Release ๅฐ Gitee โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ๅจ release ๅฎๆๅ่ฟ่ก๏ผไธ publish-scoop/aur/npm ๅนถ่ก | |
| sync-gitee-release: | |
| name: ใGiteeใSync Release | |
| needs: [check, release] | |
| if: needs.check.outputs.should_release == 'true' && needs.release.result == 'success' | |
| runs-on: ubuntu-latest | |
| env: | |
| GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }} | |
| GITEE_OWNER: vincent-zyu | |
| GITEE_REPO: winload | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Get release info from GitHub | |
| id: release_info | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| TAG="${{ needs.check.outputs.version }}" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| # ่ทๅ Release ไฟกๆฏ | |
| RELEASE_INFO=$(gh release view "$TAG" --json name,body,assets) | |
| echo "release_name=$(echo "$RELEASE_INFO" | jq -r '.name')" >> "$GITHUB_OUTPUT" | |
| echo "$RELEASE_INFO" | jq -r '.body' > release_body.md | |
| echo "$RELEASE_INFO" | jq -r '.assets[].url' > asset_urls.txt | |
| echo "๐ฆ Release: $TAG" | |
| echo "๐ Assets:" | |
| cat asset_urls.txt | |
| - name: Download GitHub Release assets | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| TAG="${{ steps.release_info.outputs.tag }}" | |
| mkdir -p assets | |
| cd assets | |
| gh release download "$TAG" --pattern "*" | |
| echo "๐ฅ Downloaded files:" | |
| ls -la | |
| - name: Create or find Gitee Release | |
| id: gitee_release | |
| run: | | |
| TAG="${{ steps.release_info.outputs.tag }}" | |
| NAME="${{ steps.release_info.outputs.release_name }}" | |
| # JSON ๅฎๅ จ่ฝฌไน release body๏ผๅค็ๆข่กใๅผๅทใๅๆๆ ็ญ๏ผ | |
| JSON_BODY=$(jq -R -s '.' < release_body.md) | |
| # 1. ็กฎไฟ tag ๅญๅจไบ Gitee | |
| if curl -s --fail "https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/tags/${TAG}?access_token=${GITEE_TOKEN}" > /dev/null 2>&1; then | |
| echo "๐ท๏ธ Tag $TAG already exists on Gitee" | |
| else | |
| echo "๐ท๏ธ Creating tag $TAG on Gitee..." | |
| curl -s -X POST --header 'Content-Type: application/json;charset=UTF-8' \ | |
| "https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/tags" \ | |
| -d "{\"access_token\":\"${GITEE_TOKEN}\",\"tag_name\":\"${TAG}\",\"refs\":\"main\",\"tag_message\":\"Release ${TAG}\"}" | |
| fi | |
| # 2. ๆฃๆฅๆฏๅฆๅทฒๆๅฏนๅบ tag ็ Release | |
| EXISTING=$(curl -s "https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/releases/tags/${TAG}?access_token=${GITEE_TOKEN}") | |
| RELEASE_ID=$(echo "$EXISTING" | jq -r '.id') | |
| if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "null" ]; then | |
| ASSET_COUNT=$(echo "$EXISTING" | jq '.assets | length') | |
| echo "โ ๏ธ Gitee release already exists (ID: $RELEASE_ID, assets: $ASSET_COUNT)" | |
| else | |
| echo "โ No existing release, creating..." | |
| # 3. ๅๅปบ Release๏ผๆณจๆ target_commitish ๆฏๅฟ ๅกซๅญๆฎต๏ผ | |
| RELEASE_PAYLOAD=$(jq -n \ | |
| --arg token "$GITEE_TOKEN" \ | |
| --arg tag "$TAG" \ | |
| --arg name "$NAME" \ | |
| --argjson body "$JSON_BODY" \ | |
| '{ | |
| access_token: $token, | |
| tag_name: $tag, | |
| name: $name, | |
| body: $body, | |
| target_commitish: "main", | |
| prerelease: false | |
| }') | |
| CREATE_RESPONSE=$(curl -s -X POST --header 'Content-Type: application/json;charset=UTF-8' \ | |
| "https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/releases" \ | |
| -d "$RELEASE_PAYLOAD") | |
| RELEASE_ID=$(echo "$CREATE_RESPONSE" | jq -r '.id') | |
| ASSET_COUNT=0 | |
| if [ "$RELEASE_ID" = "null" ] || [ -z "$RELEASE_ID" ]; then | |
| echo "โ Failed to create Gitee release" | |
| echo "Response: $CREATE_RESPONSE" | |
| exit 1 | |
| fi | |
| echo "โ Created Gitee release (ID: $RELEASE_ID)" | |
| fi | |
| echo "release_id=$RELEASE_ID" >> "$GITHUB_OUTPUT" | |
| echo "asset_count=$ASSET_COUNT" >> "$GITHUB_OUTPUT" | |
| - name: Upload assets to Gitee | |
| if: steps.gitee_release.outputs.release_id != '' && steps.gitee_release.outputs.release_id != 'null' | |
| run: | | |
| RELEASE_ID="${{ steps.gitee_release.outputs.release_id }}" | |
| # ๆถ้ดๆณๅฝๆฐ๏ผๅๆถ่พๅบ UTC ๅ ไธๆตทๆถ้ด | |
| ts() { | |
| echo "[$(date -u '+%H:%M:%S UTC') / $(TZ='Asia/Shanghai' date '+%H:%M:%S CST')]" | |
| } | |
| echo "$(ts) ๐ค Uploading assets to Gitee release (ID: $RELEASE_ID)" | |
| SUCCESS=0 | |
| FAIL=0 | |
| TOTAL=$(ls -1 assets/ | wc -l) | |
| CURRENT=0 | |
| echo "$(ts) ๐ Total files to upload: $TOTAL" | |
| for FILE in assets/*; do | |
| if [ -f "$FILE" ]; then | |
| CURRENT=$((CURRENT + 1)) | |
| FILENAME=$(basename "$FILE") | |
| FILESIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE" 2>/dev/null) | |
| FILESIZE_H=$(numfmt --to=iec $FILESIZE 2>/dev/null || echo "${FILESIZE} bytes") | |
| echo "" | |
| echo "$(ts) โโโโ [$CURRENT/$TOTAL] $FILENAME ($FILESIZE_H) โโโโ" | |
| # ้่ฏ 3 ๆฌก๏ผ่ถ ๆถ 120s๏ผ้ด้ 10s | |
| UPLOADED=false | |
| for ATTEMPT in 1 2 3; do | |
| echo "$(ts) ๐ Attempt $ATTEMPT/3 โ starting curl (max-time=120s, connect-timeout=30s)" | |
| START_TIME=$(date +%s) | |
| HTTP_CODE_FILE=$(mktemp) | |
| RESPONSE=$(curl -s --max-time 120 --connect-timeout 30 \ | |
| -w "\n%{http_code}" \ | |
| -X POST --header "Content-Type: multipart/form-data" \ | |
| -F "access_token=${GITEE_TOKEN}" \ | |
| -F "file=@${FILE}" \ | |
| "https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/releases/${RELEASE_ID}/attach_files") | |
| CURL_EXIT=$? | |
| END_TIME=$(date +%s) | |
| ELAPSED=$((END_TIME - START_TIME)) | |
| # ๅ็ฆป HTTP status code ๅ response body | |
| HTTP_CODE=$(echo "$RESPONSE" | tail -1) | |
| BODY=$(echo "$RESPONSE" | sed '$d') | |
| echo "$(ts) ๐ก curl exit=$CURL_EXIT, HTTP=$HTTP_CODE, took ${ELAPSED}s" | |
| if [ $CURL_EXIT -ne 0 ]; then | |
| case $CURL_EXIT in | |
| 28) echo "$(ts) โฑ๏ธ TIMEOUT: curl exceeded max-time (${ELAPSED}s)" ;; | |
| 7) echo "$(ts) ๐ CONNECTION REFUSED: Gitee server unreachable" ;; | |
| 56) echo "$(ts) ๐ฅ RECV ERROR: connection reset during transfer" ;; | |
| *) echo "$(ts) โ CURL ERROR $CURL_EXIT (see https://curl.se/libcurl/c/libcurl-errors.html)" ;; | |
| esac | |
| elif echo "$BODY" | jq -e '.browser_download_url' > /dev/null 2>&1; then | |
| URL=$(echo "$BODY" | jq -r '.browser_download_url') | |
| echo "$(ts) โ SUCCESS: $URL" | |
| SUCCESS=$((SUCCESS + 1)) | |
| UPLOADED=true | |
| break | |
| else | |
| echo "$(ts) โ ๏ธ UNEXPECTED RESPONSE (HTTP $HTTP_CODE):" | |
| echo "$(ts) $(echo "$BODY" | head -c 300)" | |
| fi | |
| if [ $ATTEMPT -lt 3 ]; then | |
| echo "$(ts) ๐ค Waiting 10s before retry..." | |
| sleep 10 | |
| fi | |
| done | |
| if [ "$UPLOADED" = false ]; then | |
| echo "$(ts) โ GAVE UP on $FILENAME after 3 attempts" | |
| FAIL=$((FAIL + 1)) | |
| fi | |
| fi | |
| done | |
| echo "" | |
| echo "$(ts) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ" | |
| echo "$(ts) ๐ Upload summary: $SUCCESS/$TOTAL succeeded, $FAIL failed" | |
| echo "$(ts) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ" | |
| echo "๐ Upload complete: $SUCCESS succeeded, $FAIL failed" |