release: finalize v0.1.11 and refine release download badges #165
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 | |
| env: | |
| COMMIT_MSG: ${{ github.event.head_commit.message }} | |
| run: | | |
| MSG="$COMMIT_MSG" | |
| # ไป 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 Commit | |
| 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/benchmark.sh | |
| ./benchmark/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 x86_64 (MSVC, with Npcap) โโ | |
| - target: x86_64-pc-windows-msvc | |
| os: windows-latest | |
| name: windows-x86_64-msvc | |
| binary: winload.exe | |
| asset: winload-windows-x86_64-msvc-npcap.exe | |
| # โโ Windows x86_64 (MSVC, without Npcap) โโ | |
| - target: x86_64-pc-windows-msvc | |
| os: windows-latest | |
| name: windows-x86_64-msvc-no-npcap | |
| binary: winload.exe | |
| asset: winload-windows-x86_64-msvc-no-npcap.exe | |
| no_default_features: true | |
| # โโ 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 | |
| # โโ Linux i686 (musl ้ๆ้พๆฅ, ๅ ผๅฎนๆๆ Linux) โโ | |
| - target: i686-unknown-linux-musl | |
| os: ubuntu-latest | |
| name: linux-i686 | |
| binary: winload | |
| asset: winload-linux-i686 | |
| cross_packages: musl-tools | |
| # โโ Windows ARM64 (MSVC, with Npcap) โโ | |
| - target: aarch64-pc-windows-msvc | |
| os: windows-latest | |
| name: windows-aarch64-msvc | |
| binary: winload.exe | |
| asset: winload-windows-aarch64-msvc-npcap.exe | |
| # โโ Windows ARM64 (MSVC, without Npcap) โโ | |
| - target: aarch64-pc-windows-msvc | |
| os: windows-latest | |
| name: windows-aarch64-no-npcap | |
| binary: winload.exe | |
| asset: winload-windows-aarch64-msvc-no-npcap.exe | |
| no_default_features: true | |
| # โโ 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 }} | |
| # i686 musl: download pre-built cross-compiler for static linking | |
| # (no GLIBC dependency โ runs on ANY Linux, including old distros) | |
| - name: Setup i686 musl cross-toolchain | |
| if: matrix.target == 'i686-unknown-linux-musl' | |
| run: | | |
| # Try multiple musl mirrors until one works | |
| MUSL_MIRRORS=( | |
| "https://github.com/VincentZyuApps/winload/releases/download/i686-linux-musl-cross.tgz/i686-linux-musl-cross.tgz" | |
| "https://more.musl.cc/11/x86_64-linux-musl/i686-linux-musl-cross.tgz" | |
| ) | |
| USE_GLIBC_FALLBACK=false | |
| for mirror in "${MUSL_MIRRORS[@]}"; do | |
| echo "๐ฅ Trying mirror: $mirror" | |
| if curl -fsSL --retry 2 --retry-delay 5 --connect-timeout 20 "$mirror" -o /tmp/musl-cross.tgz 2>&1; then | |
| echo "โ Downloaded from $mirror" | |
| tar xzf /tmp/musl-cross.tgz -C /tmp | |
| GCC_PATH=$(find /tmp/i686-linux-musl-cross -name 'i686-linux-musl-gcc' -type f 2>/dev/null | head -1) | |
| if [ -n "$GCC_PATH" ]; then | |
| echo "CARGO_TARGET_I686_UNKNOWN_LINUX_MUSL_LINKER=$GCC_PATH" >> $GITHUB_ENV | |
| echo "โ Using musl: $GCC_PATH" | |
| exit 0 | |
| fi | |
| fi | |
| echo "โ Failed, trying next mirror..." | |
| done | |
| # All mirrors failed - skip this build instead of using incompatible glibc | |
| echo "โ ๏ธ All musl mirrors failed, skipping i686 Linux build..." | |
| echo "I686_MUSL_SKIP=true" >> $GITHUB_ENV | |
| # 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 ้่ฆ | |
| # ๅชๅจ MSVC + default-features (npcap) ๆๅปบๆถๅฎ่ฃ (x86_64, aarch64) | |
| # MinGW ๅ --no-default-features (no-npcap) ๆๅปบ่ทณ่ฟ | |
| - name: Install Npcap SDK | |
| if: runner.os == 'Windows' && !matrix.no_default_features | |
| 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 ็ฎๅฝ | |
| # Npcap SDK 1.13: x64 ๅจ Lib/x64/, ARM64 ๅจ Lib/ARM64/ | |
| $target = "${{ matrix.target }}" | |
| if ($target -like "*aarch64*") { | |
| $libDir = "$extractPath\Lib\ARM64" | |
| } else { | |
| $libDir = "$extractPath\Lib\x64" | |
| } | |
| Write-Host "๐ Npcap SDK lib dir: $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 | |
| if: matrix.target != 'i686-unknown-linux-musl' || !env.I686_MUSL_SKIP | |
| 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 }} \ | |
| ${{ matrix.no_default_features && '--no-default-features' || '' }} | |
| # ้ช่ฏ 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, ๆ้ค i686, ๆ้ค musl ไธ่ฝฝๅคฑ่ดฅ็ๆ ๅต) | |
| - name: Build DEB & RPM packages | |
| if: contains(matrix.target, 'linux') && !contains(matrix.target, 'android') && !contains(matrix.target, 'i686') && (matrix.target != 'i686-unknown-linux-musl' || !env.I686_MUSL_SKIP) | |
| 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 | |
| # โโ RPM ๆๅปบ โโ | |
| # RPM ่ง่ไธๅ ่ฎธ็ๆฌๅทๅซ่ฟๅญ็ฌฆ '-'๏ผๅชๅ ่ฎธๅญๆฏใๆฐๅญใ'.', '_', '+', '~' ็ญๅญ็ฌฆใ | |
| # Cargo/SemVer ้ขๅๅธ็ๆฌ็จ '-'๏ผๅฆ 0.1.8-rc.25๏ผ๏ผๅฟ ้กปๆฟๆขไธบ '~'๏ผๅฆ 0.1.8~rc.25๏ผใ | |
| # '~' ๅจ RPM ไธญ่กจ็คบ"ๆฉไบ"ๅๅๆญฃๅผ็ๆฌ๏ผ่ฏญไนไธ้ขๅๅธไธ่ดใ | |
| # DEB ่ชๅจๅฐ '-' ่ฝฌไธบ '~'๏ผcargo-deb ๅ ็ฝฎๅค็๏ผ๏ผRPM ้่ฆๆๅจ่ฝฌๆขใ | |
| # cargo generate-rpm ็ --set-metadata ๆ ๆณๅจ็ๆฌๆ ก้ชๅ่ฆ็ version๏ผ | |
| # ๅ ๆญค้่ฆไธดๆถ sed ๆฟๆข Cargo.toml ไธญ็็ๆฌๅท๏ผๆๅปบๅฎๆๅ git restore ๆขๅคใ | |
| # โ ๏ธ ็จ tr ๆฟไปฃ bash ๅๆฐๅฑๅผ ${var//-/~}๏ผๅ ไธบ bash ๅจ CI ็ฏๅขไธ | |
| # ไผๅฐ '~' ๅฑๅผไธบ $HOME๏ผๅฆ /home/runner๏ผ๏ผๆฑกๆ็ๆฌๅทใ | |
| RPM_VERSION="${VERSION#v}" | |
| RPM_VERSION=$(echo "$RPM_VERSION" | tr '-' '~') | |
| echo "๐ RPM version (before sed): ${RPM_VERSION}" | |
| echo "๐ Cargo.toml version (before): $(grep '^version' Cargo.toml)" | |
| sed -i "s|^version = .*|version = \"${RPM_VERSION}\"|" Cargo.toml | |
| echo "๐ Cargo.toml version (after ): $(grep '^version' Cargo.toml)" | |
| echo "๐ฆ Building RPM package for ${{ matrix.target }}..." | |
| cargo generate-rpm --target ${{ matrix.target }} | |
| git restore Cargo.toml | |
| 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') && !contains(matrix.target, 'i686') && (matrix.target != 'i686-unknown-linux-musl' || !env.I686_MUSL_SKIP) | |
| 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') && !contains(matrix.target, 'i686') && (matrix.target != 'i686-unknown-linux-musl' || !env.I686_MUSL_SKIP) | |
| 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-msvc-npcap.exe -> winload-windows-x86_64-msvc-npcap-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}" | |
| PLAIN_VER="${VERSION#v}" | |
| PYPI_VER=$(echo "$PLAIN_VER" | sed 's/-rc\./rc/') | |
| # ็จ sed ๅกซๅ ๆจกๆฟๅ ไฝ็ฌฆ๏ผ็ๆ้ๆ้จๅ๏ผไธ่ฝฝ่กจ + ๅฎ่ฃ ๆๅผ๏ผ | |
| sed \ | |
| -e "s|__VERSION__|${VERSION}|g" \ | |
| -e "s|__PLAIN_VER__|${PLAIN_VER}|g" \ | |
| -e "s|__PYPI_VER__|${PYPI_VER}|g" \ | |
| -e "s|__REPO__|${REPO}|g" \ | |
| -e "s|__BASE_URL__|${BASE_URL}|g" \ | |
| .github/release_template.md > release_notes.md | |
| # ๆฅๆพไธไธไธช release tag๏ผๆ้คๅฝๅ็ๆฌ๏ผ | |
| PREV_TAG=$(git tag --sort=-v:refname | grep -v "^${VERSION}$" | head -1) | |
| # ่ฟฝๅ changelog + build info | |
| { | |
| 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 (GitHub) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ่งฆๅๆนๅผ: | |
| # - "publish from release" โ ไปๅทฒๆ Release ๆๅไบ่ฟๅถ๏ผไป ๆดๆฐ Scoop | |
| # - "build publish" โ ๆๅปบ + Release + ๆดๆฐ Scoop๏ผๅ จๅฅ๏ผ | |
| publish-scoop-github: | |
| 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-msvc-npcap-${VERSION}.exe" \ | |
| || { echo "โ Failed to download x64 binary"; exit 1; } | |
| # ไธ่ฝฝ Windows ARM64 | |
| curl -fSL -o winload-windows-aarch64.exe \ | |
| "${BASE_URL}/winload-windows-aarch64-msvc-npcap-${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-msvc-npcap-${VERSION}.exe", | |
| "hash": "${HASH_X64}", | |
| "bin": [["winload-windows-x86_64-msvc-npcap-${VERSION}.exe", "win-nload"]] | |
| }, | |
| "arm64": { | |
| "url": "${BASE_URL}/winload-windows-aarch64-msvc-npcap-${VERSION}.exe", | |
| "hash": "${HASH_ARM64}", | |
| "bin": [["winload-windows-aarch64-msvc-npcap-${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-msvc-npcap-v\$version.exe" | |
| }, | |
| "arm64": { | |
| "url": "https://github.com/${REPO}/releases/download/v\$version/winload-windows-aarch64-msvc-npcap-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 "ci: ๐จ Update winload to ${VERSION}" || echo "No changes to commit" | |
| git push | |
| echo "โ Scoop bucket updated!" | |
| # โโ ๆดๆฐ Scoop Bucket (Gitee) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ็ๆๆๅ Gitee Releases ็ scoop manifest๏ผๆจ้ๅฐ Gitee | |
| publish-scoop-gitee: | |
| name: ใGitee็ ไบใUpdate Scoop Bucket | |
| 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: Download Windows binaries from GitHub Releases (for hash) | |
| 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 GitHub release..." | |
| curl -fSL -o winload-windows-x86_64.exe \ | |
| "${BASE_URL}/winload-windows-x86_64-msvc-npcap-${VERSION}.exe" \ | |
| || { echo "โ Failed to download x64 binary"; exit 1; } | |
| curl -fSL -o winload-windows-aarch64.exe \ | |
| "${BASE_URL}/winload-windows-aarch64-msvc-npcap-${VERSION}.exe" \ | |
| || { echo "โ Failed to download arm64 binary"; exit 1; } | |
| echo "โ Downloaded:" | |
| ls -lh winload-windows-*.exe | |
| - name: Generate Gitee Scoop manifest | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| SCOOP_VERSION="${VERSION#v}" | |
| GITEE_BASE_URL="https://gitee.com/vincent-zyu/winload/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://gitee.com/vincent-zyu/winload", | |
| "license": "MIT", | |
| "architecture": { | |
| "64bit": { | |
| "url": "${GITEE_BASE_URL}/winload-windows-x86_64-msvc-npcap-${VERSION}.exe", | |
| "hash": "${HASH_X64}", | |
| "bin": [["winload-windows-x86_64-msvc-npcap-${VERSION}.exe", "win-nload"]] | |
| }, | |
| "arm64": { | |
| "url": "${GITEE_BASE_URL}/winload-windows-aarch64-msvc-npcap-${VERSION}.exe", | |
| "hash": "${HASH_ARM64}", | |
| "bin": [["winload-windows-aarch64-msvc-npcap-${VERSION}.exe", "win-nload"]] | |
| } | |
| }, | |
| "checkver": { | |
| "url": "https://gitee.com/vincent-zyu/winload/releases" | |
| }, | |
| "autoupdate": { | |
| "architecture": { | |
| "64bit": { | |
| "url": "https://gitee.com/vincent-zyu/winload/releases/download/v\$version/winload-windows-x86_64-msvc-npcap-v\$version.exe" | |
| }, | |
| "arm64": { | |
| "url": "https://gitee.com/vincent-zyu/winload/releases/download/v\$version/winload-windows-aarch64-msvc-npcap-v\$version.exe" | |
| } | |
| } | |
| } | |
| } | |
| EOF | |
| echo "๐ Generated Gitee winload.json:" | |
| cat winload.json | |
| - name: Push to Gitee scoop-bucket repo | |
| env: | |
| GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| git clone https://vincent-zyu:${GITEE_TOKEN}@gitee.com/vincent-zyu/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 "ci: ๐จ Update winload to ${VERSION}" || echo "No changes to commit" | |
| git push | |
| echo "โ Gitee Scoop bucket updated!" | |
| # โโ ๆดๆฐ Homebrew Tap (GitHub) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ็ๆๆๅ GitHub Releases ็ homebrew formula๏ผๆจ้ๅฐ GitHub homebrew-tap | |
| publish-homebrew-github: | |
| name: Update Homebrew Tap | |
| 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: 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}" | |
| # ไธ่ฝฝๆๆๅนณๅฐ็ไบ่ฟๅถ็จไบ่ฎก็ฎ SHA256 | |
| PLATFORMS=( | |
| "winload-linux-x86_64:${BASE_URL}/winload-linux-x86_64-${VERSION}" | |
| "winload-linux-aarch64:${BASE_URL}/winload-linux-aarch64-${VERSION}" | |
| "winload-macos-x86_64:${BASE_URL}/winload-macos-x86_64-${VERSION}" | |
| "winload-macos-aarch64:${BASE_URL}/winload-macos-aarch64-${VERSION}" | |
| ) | |
| for entry in "${PLATFORMS[@]}"; do | |
| NAME="${entry%%:*}" | |
| URL="${entry##*:}" | |
| echo "๐ฅ Downloading $NAME..." | |
| curl -fSL -o "$NAME" "$URL" || echo "โ ๏ธ Failed to download $NAME (may not exist)" | |
| done | |
| echo "โ Downloaded:" | |
| ls -lh winload-* 2>/dev/null || echo "No binaries downloaded" | |
| - name: Generate Homebrew formula | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| TAGVER="${VERSION#v}" # ๅปๆ v ๅ็ผ | |
| REPO="${{ github.repository }}" | |
| BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}" | |
| mkdir -p Formula | |
| # ่ฎก็ฎๅๅนณๅฐ SHA256 | |
| SHA_LINUX_X64=$(sha256sum winload-linux-x86_64 2>/dev/null | awk '{print $1}' || echo "") | |
| SHA_LINUX_ARM64=$(sha256sum winload-linux-aarch64 2>/dev/null | awk '{print $1}' || echo "") | |
| SHA_MACOS_X64=$(sha256sum winload-macos-x86_64 2>/dev/null | awk '{print $1}' || echo "") | |
| SHA_MACOS_ARM64=$(sha256sum winload-macos-aarch64 2>/dev/null | awk '{print $1}' || echo "") | |
| cat > Formula/winload.rb << HEREDOC | |
| class Winload < Formula | |
| desc "Network Load Monitor - nload-like TUI tool for Windows/Linux/macOS" | |
| homepage "https://github.com/${REPO}" | |
| license "MIT" | |
| version "${TAGVER}" | |
| # Dynamic URL generation per platform | |
| if OS.mac? && Hardware::CPU.intel? | |
| url "${BASE_URL}/winload-macos-x86_64-${VERSION}" | |
| sha256 "${SHA_MACOS_X64}" | |
| elsif OS.mac? | |
| url "${BASE_URL}/winload-macos-aarch64-${VERSION}" | |
| sha256 "${SHA_MACOS_ARM64}" | |
| elsif OS.linux? && Hardware::CPU.intel? | |
| url "${BASE_URL}/winload-linux-x86_64-${VERSION}" | |
| sha256 "${SHA_LINUX_X64}" | |
| elsif OS.linux? | |
| url "${BASE_URL}/winload-linux-aarch64-${VERSION}" | |
| sha256 "${SHA_LINUX_ARM64}" | |
| end | |
| def install | |
| bin.install Dir["winload-*"].first => "winload" | |
| end | |
| test do | |
| system "#{bin}/winload", "--version" | |
| end | |
| end | |
| HEREDOC | |
| # ็งป้ค heredoc ็ผฉ่ฟ | |
| sed -i 's/^ //' Formula/winload.rb | |
| echo "๐ Generated Formula/winload.rb:" | |
| cat Formula/winload.rb | |
| - name: Push to homebrew-tap repo | |
| env: | |
| GH_TOKEN: ${{ secrets.SCOOP_BUCKET_TOKEN }} # ๅค็จๅทฒๆ token | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| git clone https://x-access-token:${GH_TOKEN}@github.com/VincentZyuApps/homebrew-tap.git brew-repo | |
| cp Formula/winload.rb brew-repo/Formula/winload.rb | |
| cd brew-repo | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Formula/winload.rb | |
| git commit -m "ci: ๐บ Update winload to ${VERSION}" || echo "No changes to commit" | |
| git push | |
| echo "โ Homebrew tap updated!" | |
| # โโ ๆดๆฐ Homebrew Tap (Gitee) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # ็ๆๆๅ Gitee Releases ็ homebrew formula๏ผๆจ้ๅฐ Gitee homebrew-tap | |
| publish-homebrew-gitee: | |
| name: ใGitee็ ไบใUpdate Homebrew Tap | |
| 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: Fetch latest release info from Gitee | |
| env: | |
| GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| GITEE_BASE_URL="https://gitee.com/vincent-zyu/winload/releases/download/${VERSION}" | |
| echo "๐ฆ Target version: ${VERSION}" | |
| # ไธ่ฝฝๆๆๅนณๅฐ็ไบ่ฟๅถ็จไบ่ฎก็ฎ SHA256 | |
| PLATFORMS=( | |
| "winload-linux-x86_64:${GITEE_BASE_URL}/winload-linux-x86_64-${VERSION}" | |
| "winload-linux-aarch64:${GITEE_BASE_URL}/winload-linux-aarch64-${VERSION}" | |
| "winload-macos-x86_64:${GITEE_BASE_URL}/winload-macos-x86_64-${VERSION}" | |
| "winload-macos-aarch64:${GITEE_BASE_URL}/winload-macos-aarch64-${VERSION}" | |
| ) | |
| for entry in "${PLATFORMS[@]}"; do | |
| NAME="${entry%%:*}" | |
| URL="${entry##*:}" | |
| echo "๐ฅ Downloading $NAME from Gitee..." | |
| curl -fSL -o "$NAME" "$URL" || echo "โ ๏ธ Failed to download $NAME (may not exist)" | |
| done | |
| echo "โ Downloaded:" | |
| ls -lh winload-* 2>/dev/null || echo "No binaries downloaded" | |
| - name: Generate Homebrew formula (Gitee) | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| TAGVER="${VERSION#v}" | |
| GITEE_BASE_URL="https://gitee.com/vincent-zyu/winload/releases/download/${VERSION}" | |
| mkdir -p Formula | |
| # ่ฎก็ฎๅๅนณๅฐ SHA256 | |
| SHA_LINUX_X64=$(sha256sum winload-linux-x86_64 2>/dev/null | awk '{print $1}' || echo "") | |
| SHA_LINUX_ARM64=$(sha256sum winload-linux-aarch64 2>/dev/null | awk '{print $1}' || echo "") | |
| SHA_MACOS_X64=$(sha256sum winload-macos-x86_64 2>/dev/null | awk '{print $1}' || echo "") | |
| SHA_MACOS_ARM64=$(sha256sum winload-macos-aarch64 2>/dev/null | awk '{print $1}' || echo "") | |
| cat > Formula/winload.rb << HEREDOC | |
| class Winload < Formula | |
| desc "Network Load Monitor - nload-like TUI tool for Windows/Linux/macOS" | |
| homepage "https://gitee.com/vincent-zyu/winload" | |
| license "MIT" | |
| version "${TAGVER}" | |
| # Dynamic URL generation per platform (Gitee) | |
| if OS.mac? && Hardware::CPU.intel? | |
| url "${GITEE_BASE_URL}/winload-macos-x86_64-${VERSION}" | |
| sha256 "${SHA_MACOS_X64}" | |
| elsif OS.mac? | |
| url "${GITEE_BASE_URL}/winload-macos-aarch64-${VERSION}" | |
| sha256 "${SHA_MACOS_ARM64}" | |
| elsif OS.linux? && Hardware::CPU.intel? | |
| url "${GITEE_BASE_URL}/winload-linux-x86_64-${VERSION}" | |
| sha256 "${SHA_LINUX_X64}" | |
| elsif OS.linux? | |
| url "${GITEE_BASE_URL}/winload-linux-aarch64-${VERSION}" | |
| sha256 "${SHA_LINUX_ARM64}" | |
| end | |
| def install | |
| bin.install Dir["winload-*"].first => "winload" | |
| end | |
| test do | |
| system "#{bin}/winload", "--version" | |
| end | |
| end | |
| HEREDOC | |
| # ็งป้ค heredoc ็ผฉ่ฟ | |
| sed -i 's/^ //' Formula/winload.rb | |
| echo "๐ Generated Formula/winload.rb (Gitee):" | |
| cat Formula/winload.rb | |
| - name: Push to Gitee homebrew-tap repo | |
| env: | |
| GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.check.outputs.version }}" | |
| git clone https://vincent-zyu:${GITEE_TOKEN}@gitee.com/vincent-zyu/homebrew-tap.git brew-repo | |
| cp Formula/winload.rb brew-repo/Formula/winload.rb | |
| cd brew-repo | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Formula/winload.rb | |
| git commit -m "ci: ๐บ Update winload to ${VERSION}" || echo "No changes to commit" | |
| git push | |
| echo "โ Gitee Homebrew tap 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 "ci: ๐ผ Update to ${PKGVER}" || echo "No changes to commit" | |
| git push | |
| echo "โ winload-rust-bin updated!" | |
| # โโ ๅๅธๅฐ npm๏ผscoped: @vincentzyuapps/winload๏ผโโโโโโโโโโโโโโโโโ | |
| # ๅ็ๅ esbuild / @biomejs/biome / turbo: | |
| # 1. ไธบๆฏไธชๅนณๅฐๅๅธไธไธชๅชๅซไบ่ฟๅถ็ๅ (os/cpu ๅญๆฎต่ฎฉ npm ่ชๅจ็ญ้) | |
| # 2. ไธปๅ @vincentzyuapps/winload ้่ฟ optionalDependencies ๅผ็จๅฎไปฌ | |
| # 3. ็จๆท npm install ๆถๅชไธ่ฝฝๅน้ ๅฝๅๅนณๅฐ็้ฃไธไธช | |
| publish-npm-scope: | |
| name: Publish to npm (scoped) | |
| needs: [check, release] | |
| if: always() && needs.check.outputs.should_publish == 'true' && (needs.release.result == 'success' || needs.release.result == 'skipped') | |
| permissions: | |
| id-token: write | |
| packages: write | |
| contents: read | |
| 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-msvc-npcap-${VERSION}.exe" | |
| curl -fSL -o artifacts/winload-windows-aarch64.exe "${BASE_URL}/winload-windows-aarch64-msvc-npcap-${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})" | |
| 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 --provenance --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" | |
| cp readme.md npm/winload-rust-bin/readme.md | |
| cd npm/winload-rust-bin | |
| 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 --provenance --access public --tag "${NPM_TAG}" | |
| echo "โ Scoped package published!" | |
| - name: Upload npm packages for downstream jobs | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: npm-packages | |
| path: | | |
| npm-platforms/ | |
| npm/winload-rust-bin/ | |
| if-no-files-found: error | |
| # โโ ๅๅธๅฐ npm๏ผunscoped: winload-rust-bin๏ผโโโโโโโโโโโโโโโโโ | |
| publish-npm-unscope: | |
| name: Publish to npm (unscoped) | |
| needs: [check, release, publish-npm-scope] | |
| if: always() && needs.check.outputs.should_publish == 'true' && (needs.release.result == 'success' || needs.release.result == 'skipped') | |
| permissions: | |
| id-token: write | |
| contents: read | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: npm-packages | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Publish winload-rust-bin (unscoped) to npm | |
| run: | | |
| cd npm/winload-rust-bin | |
| node -e " | |
| const p = require('./package.json'); | |
| p.name = 'winload-rust-bin'; | |
| require('fs').writeFileSync('./package.json', JSON.stringify(p, null, 2) + '\n'); | |
| " | |
| npm publish --provenance --access public --tag latest | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| # โโ ๅๆญฅๅๅธๅฐ GitHub Packages (npm.pkg.github.com) โโโโโโโโ | |
| publish-npm-github-packages: | |
| name: Publish to GitHub Packages for npm | |
| needs: [check, release, publish-npm-scope] | |
| if: always() && needs.check.outputs.should_publish == 'true' && (needs.release.result == 'success' || needs.release.result == 'skipped') | |
| permissions: | |
| contents: read | |
| packages: write | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: npm-packages | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Publish platform packages to GitHub Packages | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| 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 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 | |
| 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 python/readme.md | |
| - name: Build package | |
| working-directory: python | |
| run: | | |
| uv build | |
| - name: Publish to PyPI | |
| working-directory: python | |
| 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 File | |
| 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) | |
| CURL="curl -s -k --retry 3 --retry-delay 5 --retry-all-errors" | |
| # 1. ็กฎไฟ tag ๅญๅจไบ Gitee | |
| if $CURL --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 -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 "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 -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 ๆฌก๏ผ่ถ ๆถ 1200s (20min)๏ผ้ด้ 10s | |
| # Gitee ไธไผ ้ๅบฆ็บฆ 10KB/s๏ผ3MB ๆไปถ้่ฆ ~300s | |
| UPLOADED=false | |
| for ATTEMPT in 1 2 3; do | |
| echo "$(ts) ๐ Attempt $ATTEMPT/3 โ starting curl (max-time=1200s, connect-timeout=30s)" | |
| START_TIME=$(date +%s) | |
| HTTP_CODE_FILE=$(mktemp) | |
| RESPONSE=$(curl -s -k --retry 3 --retry-delay 10 --retry-all-errors \ | |
| --max-time 1200 --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" |