Skip to content

feat: add Gitee install script & verbose upload logging #83

feat: add Gitee install script & verbose upload logging

feat: add Gitee install script & verbose upload logging #83

Workflow file for this run

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"