From a838ae75a77e96622a27dc3e57ccb225967de390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BF=AE=E9=9B=A8?= Date: Tue, 7 Jul 2026 21:37:02 +0800 Subject: [PATCH] ci: unblock npm release from Gitee mirror --- .github/workflows/publish-npm-release.yml | 71 ++++++++++ .github/workflows/release.yml | 93 +++++++++++-- .workflow/gitee-release.yml | 51 ++++++++ scripts/release/build-and-publish-gitee.sh | 50 +++++++ scripts/release/publish-gitee-local.sh | 144 +++++++++++++++++++++ scripts/release/sync-to-gitee.sh | 31 ++++- 6 files changed, 422 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/publish-npm-release.yml create mode 100644 .workflow/gitee-release.yml create mode 100755 scripts/release/build-and-publish-gitee.sh create mode 100755 scripts/release/publish-gitee-local.sh diff --git a/.github/workflows/publish-npm-release.yml b/.github/workflows/publish-npm-release.yml new file mode 100644 index 00000000..20cd62c8 --- /dev/null +++ b/.github/workflows/publish-npm-release.yml @@ -0,0 +1,71 @@ +name: Publish npm release + +on: + workflow_dispatch: + inputs: + version: + description: "Release tag to publish to npm (e.g. v1.0.48)" + required: true + type: string + +permissions: + contents: read + +jobs: + publish-npm: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Download GitHub release assets + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eu + mkdir -p dist + gh release download "${{ inputs.version }}" \ + --repo "${{ github.repository }}" \ + --dir dist \ + --pattern 'dws-*' \ + --pattern 'checksums.txt' \ + --clobber + ls -la dist + + - name: Stage npm package + run: | + set -eu + version="${{ inputs.version }}" + semver="${version#v}" + pkg_root="dist/npm/dingtalk-workspace-cli" + rm -rf "$pkg_root" + mkdir -p "$pkg_root/assets" "$pkg_root/bin" + cp build/npm/install.js "$pkg_root/install.js" + cp build/npm/bin/dws.js "$pkg_root/bin/dws.js" + cp build/npm/README.md "$pkg_root/README.md" + sed "s|__VERSION__|${semver}|g" build/npm/package.json.tmpl > "$pkg_root/package.json" + cp dist/dws-* "$pkg_root/assets/" + cp dist/checksums.txt "$pkg_root/assets/" + test -f "$pkg_root/assets/dws-skills.zip" + cat "$pkg_root/package.json" + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + registry-url: "https://registry.npmjs.org" + + - name: Publish stable to npm + if: ${{ github.repository_owner == 'DingTalk-Real-AI' && !contains(inputs.version, '-') }} + working-directory: dist/npm/dingtalk-workspace-cli + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Publish prerelease to npm beta + if: ${{ github.repository_owner == 'DingTalk-Real-AI' && contains(inputs.version, '-') }} + working-directory: dist/npm/dingtalk-workspace-cli + run: npm publish --access public --tag beta + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1542a017..1f7f0ded 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,12 +5,18 @@ on: tags: - "v*" workflow_dispatch: + inputs: + repair_npm_version: + description: "Only publish an existing release to npm, e.g. v1.0.48" + required: false + type: string permissions: contents: write jobs: release: + if: ${{ github.event_name != 'workflow_dispatch' || inputs.repair_npm_version == '' }} runs-on: ubuntu-latest # 60 (not 30): mirroring every release asset to Gitee is slow; 30 min cut the # Gitee step off mid-upload on the v1.0.42 release. The Gitee step is now also @@ -76,17 +82,6 @@ jobs: OSS_BUCKET: ${{ secrets.OSS_BUCKET }} OSS_PREFIX: ${{ secrets.OSS_PREFIX }} - - name: Mirror release to Gitee (China) - # 把 release 附件(二进制/校验和/skills 包)镜像到 Gitee release,供 install.sh - # 的 DWS_GITEE_REPO 开关消费(仓库代码由 Gitee 仓库镜像功能自动同步,附件不在其内)。 - # 脚本自带门控:未配置 GITEE_TOKEN / GITEE_REPO 时优雅跳过,不影响海外发布。 - run: ./scripts/release/sync-to-gitee.sh - env: - VERSION: ${{ github.ref_name }} - GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }} - GITEE_USER: ${{ secrets.GITEE_USER }} - GITEE_REPO: ${{ secrets.GITEE_REPO }} - - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -94,7 +89,8 @@ jobs: registry-url: "https://registry.npmjs.org" - name: Publish stable to npm - # 只有官方仓库发 npm;fork(dev 预览)没有 NPM_TOKEN,跳过以免红叉 + # 只有官方仓库发 npm;fork(dev 预览)没有 NPM_TOKEN,跳过以免红叉。 + # 必须在 Gitee mirror 前发布:Gitee 附件上传偶发长时间挂住,不能阻塞 npm/latest。 if: ${{ github.repository_owner == 'DingTalk-Real-AI' && !contains(github.ref_name, '-') }} working-directory: dist/npm/dingtalk-workspace-cli run: npm publish --access public @@ -108,3 +104,76 @@ jobs: run: npm publish --access public --tag beta env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Mirror release to Gitee (China) + # 把 release 附件(二进制/校验和/skills 包)镜像到 Gitee release,供 install.sh + # 的 DWS_GITEE_REPO 开关消费(仓库代码由 Gitee 仓库镜像功能自动同步,附件不在其内)。 + # 默认关闭:国内 release 应由 Gitee 侧本地构建发布,避免 GitHub -> Gitee 跨境传大包卡住。 + # 仅在需要临时补救时设置 repo variable ENABLE_GITEE_UPLOAD_FALLBACK=true。 + if: ${{ vars.ENABLE_GITEE_UPLOAD_FALLBACK == 'true' }} + timeout-minutes: 20 + run: ./scripts/release/sync-to-gitee.sh + env: + VERSION: ${{ github.ref_name }} + GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }} + GITEE_USER: ${{ secrets.GITEE_USER }} + GITEE_REPO: ${{ secrets.GITEE_REPO }} + + repair-npm: + if: ${{ github.event_name == 'workflow_dispatch' && inputs.repair_npm_version != '' }} + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Download GitHub release assets + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eu + mkdir -p dist + gh release download "${{ inputs.repair_npm_version }}" \ + --repo "${{ github.repository }}" \ + --dir dist \ + --pattern 'dws-*' \ + --pattern 'checksums.txt' \ + --clobber + ls -la dist + + - name: Stage npm package + run: | + set -eu + version="${{ inputs.repair_npm_version }}" + semver="${version#v}" + pkg_root="dist/npm/dingtalk-workspace-cli" + rm -rf "$pkg_root" + mkdir -p "$pkg_root/assets" "$pkg_root/bin" + cp build/npm/install.js "$pkg_root/install.js" + cp build/npm/bin/dws.js "$pkg_root/bin/dws.js" + cp build/npm/README.md "$pkg_root/README.md" + sed "s|__VERSION__|${semver}|g" build/npm/package.json.tmpl > "$pkg_root/package.json" + cp dist/dws-* "$pkg_root/assets/" + cp dist/checksums.txt "$pkg_root/assets/" + test -f "$pkg_root/assets/dws-skills.zip" + cat "$pkg_root/package.json" + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + registry-url: "https://registry.npmjs.org" + + - name: Publish stable to npm + if: ${{ github.repository_owner == 'DingTalk-Real-AI' && !contains(inputs.repair_npm_version, '-') }} + working-directory: dist/npm/dingtalk-workspace-cli + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Publish prerelease to npm beta + if: ${{ github.repository_owner == 'DingTalk-Real-AI' && contains(inputs.repair_npm_version, '-') }} + working-directory: dist/npm/dingtalk-workspace-cli + run: npm publish --access public --tag beta + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.workflow/gitee-release.yml b/.workflow/gitee-release.yml new file mode 100644 index 00000000..a1f64646 --- /dev/null +++ b/.workflow/gitee-release.yml @@ -0,0 +1,51 @@ +name: Gitee Release + +on: + push: + tags: + - "v*" + workflow_dispatch: + inputs: + version: + description: "Release tag to build on Gitee, e.g. v1.0.48" + required: false + type: string + +jobs: + release: + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install packaging tools + run: | + set -eu + sudo apt-get update + sudo apt-get install -y zip unzip curl + + - name: Install rcodesign + run: | + set -eu + RCS_VERSION="0.27.0" + curl -fsSL -o /tmp/rcodesign.tar.gz \ + "https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F${RCS_VERSION}/apple-codesign-${RCS_VERSION}-x86_64-unknown-linux-musl.tar.gz" + mkdir -p /tmp/rcodesign + tar -xzf /tmp/rcodesign.tar.gz -C /tmp/rcodesign --strip-components=1 + sudo install -m 0755 /tmp/rcodesign/rcodesign /usr/local/bin/rcodesign + rcodesign --version + + - name: Build and publish Gitee release + env: + VERSION: ${{ inputs.version || github.ref_name }} + GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }} + GITEE_REPO: DingTalk-Real-AI/dingtalk-workspace-cli + run: ./scripts/release/build-and-publish-gitee.sh diff --git a/scripts/release/build-and-publish-gitee.sh b/scripts/release/build-and-publish-gitee.sh new file mode 100755 index 00000000..766924de --- /dev/null +++ b/scripts/release/build-and-publish-gitee.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Build release artifacts locally in the current runner and publish them to Gitee. + +set -eu + +ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)" +SCRIPT_ROOT="$ROOT" +cd "$SCRIPT_ROOT" + +VERSION="${VERSION:-$(git describe --tags --exact-match 2>/dev/null || true)}" +[ -n "$VERSION" ] || { + echo "error: VERSION is required or HEAD must be an exact release tag" >&2 + exit 1 +} +case "$VERSION" in + v*) TAG="$VERSION"; SEMVER="${VERSION#v}" ;; + *) TAG="v$VERSION"; SEMVER="$VERSION" ;; +esac + +git fetch --tags origin "refs/tags/${TAG}:refs/tags/${TAG}" >/dev/null 2>&1 || true +target_commit="$(git rev-parse "${TAG}^{commit}" 2>/dev/null || true)" +[ -n "$target_commit" ] || { + echo "error: could not resolve tag ${TAG}" >&2 + exit 1 +} + +current_commit="$(git rev-parse HEAD)" +WORKDIR="$SCRIPT_ROOT" +cleanup_worktree() { + if [ "$WORKDIR" != "$SCRIPT_ROOT" ]; then + git -C "$SCRIPT_ROOT" worktree remove --force "$WORKDIR" >/dev/null 2>&1 || rm -rf "$WORKDIR" + fi +} +trap cleanup_worktree EXIT + +if [ "$current_commit" != "$target_commit" ]; then + WORKDIR="$(mktemp -d)" + rm -rf "$WORKDIR" + git worktree add --detach "$WORKDIR" "$TAG" + mkdir -p "$WORKDIR/scripts/release" + cp "$SCRIPT_ROOT/scripts/release/publish-gitee-local.sh" "$WORKDIR/scripts/release/publish-gitee-local.sh" + chmod +x "$WORKDIR/scripts/release/publish-gitee-local.sh" +fi + +cd "$WORKDIR" + +echo "==> Building ${TAG} locally for Gitee" +VERSION="$SEMVER" ./scripts/dev/build-all.sh +DWS_PACKAGE_VERSION="$TAG" ./scripts/release/post-goreleaser.sh +VERSION="$TAG" ./scripts/release/publish-gitee-local.sh diff --git a/scripts/release/publish-gitee-local.sh b/scripts/release/publish-gitee-local.sh new file mode 100755 index 00000000..27ab9ff2 --- /dev/null +++ b/scripts/release/publish-gitee-local.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# Publish locally-built release artifacts to the matching Gitee release. +# +# Intended to run inside Gitee Go after building artifacts in China. This avoids +# the unreliable GitHub Actions -> Gitee cross-border upload path. + +set -eu + +DIST_DIR="${DIST_DIR:-dist}" +GITEE_API="${GITEE_API:-https://gitee.com/api/v5}" +GITEE_REPO="${GITEE_REPO:-DingTalk-Real-AI/dingtalk-workspace-cli}" +GITEE_TOKEN="${GITEE_TOKEN:-${GITEE_ACCESS_TOKEN:-}}" +GITEE_CURL_CONNECT_TIMEOUT="${GITEE_CURL_CONNECT_TIMEOUT:-15}" +GITEE_CURL_MAX_TIME="${GITEE_CURL_MAX_TIME:-120}" +GITEE_UPLOAD_MAX_TIME="${GITEE_UPLOAD_MAX_TIME:-300}" +GITEE_UPLOAD_RETRIES="${GITEE_UPLOAD_RETRIES:-3}" +GITEE_UPLOAD_RETRY_DELAY="${GITEE_UPLOAD_RETRY_DELAY:-10}" + +err() { + printf 'error: %s\n' "$*" >&2 + exit 1 +} + +[ -n "$GITEE_TOKEN" ] || err "GITEE_TOKEN is required" +[ -d "$DIST_DIR" ] || err "dist dir not found: $DIST_DIR" + +VERSION="${VERSION:-$(git describe --tags --exact-match 2>/dev/null || true)}" +[ -n "$VERSION" ] || err "VERSION is required or HEAD must be an exact tag" +case "$VERSION" in + v*) ;; + *) VERSION="v$VERSION" ;; +esac + +OWNER="${GITEE_REPO%%/*}" +NAME="${GITEE_REPO##*/}" +base="${GITEE_API}/repos/${OWNER}/${NAME}" + +sha256_of() { + if command -v sha256sum >/dev/null 2>&1; then sha256sum ${1:+"$1"} | awk '{print $1}' + else shasum -a 256 ${1:+"$1"} | awk '{print $1}' + fi +} + +api_get() { + curl -fsSL --connect-timeout "$GITEE_CURL_CONNECT_TIMEOUT" --max-time "$GITEE_CURL_MAX_TIME" "$@" +} + +echo "📦 Publishing local artifacts for ${VERSION} to Gitee ${GITEE_REPO}" + +target_commit="$(git rev-parse "${VERSION}^{commit}" 2>/dev/null || git rev-parse HEAD)" + +rel_json="$(api_get "${base}/releases/tags/${VERSION}?access_token=${GITEE_TOKEN}" 2>/dev/null || true)" +release_id="$(printf '%s' "$rel_json" | grep -o '"id":[ ]*[0-9]*' | head -1 | grep -o '[0-9]*' || true)" + +if [ -z "$release_id" ]; then + echo " No Gitee release for ${VERSION} yet — creating it." + rel_json="$(curl -fsSL --connect-timeout "$GITEE_CURL_CONNECT_TIMEOUT" --max-time "$GITEE_CURL_MAX_TIME" \ + -X POST "${base}/releases" \ + -F "access_token=${GITEE_TOKEN}" \ + -F "tag_name=${VERSION}" \ + -F "name=${VERSION}" \ + -F "body=Gitee-local build of ${VERSION} for China users." \ + -F "target_commitish=${target_commit}" 2>/dev/null || true)" + release_id="$(printf '%s' "$rel_json" | grep -o '"id":[ ]*[0-9]*' | head -1 | grep -o '[0-9]*' || true)" +fi +[ -n "$release_id" ] || err "could not get/create Gitee release for ${VERSION}. Response: ${rel_json}" +echo " Gitee release id = ${release_id}" + +assets_map="$(api_get "${base}/releases/${release_id}/attach_files?access_token=${GITEE_TOKEN}" 2>/dev/null \ + | python3 -c 'import json,sys +try: + data=json.load(sys.stdin) + rows=data if isinstance(data,list) else data.get("attach_files",[]) + for a in rows: + n=a.get("name",""); i=a.get("id",""); u=a.get("browser_download_url","") + if n and i!="": + print("%s\t%s\t%s" % (n, i, u)) +except Exception: + pass' 2>/dev/null || true)" + +gitee_attach() { + file="$1" + fn="$(basename "$file")" + attempt=1 + while [ "$attempt" -le "$GITEE_UPLOAD_RETRIES" ]; do + response="$(curl -fsS --connect-timeout "$GITEE_CURL_CONNECT_TIMEOUT" --max-time "$GITEE_UPLOAD_MAX_TIME" \ + --retry 2 --retry-delay 5 --retry-all-errors \ + -X POST "${base}/releases/${release_id}/attach_files" \ + -F "access_token=${GITEE_TOKEN}" -F "file=@${file}" 2>&1 || true)" + if printf '%s' "$response" | grep -q '"browser_download_url"'; then + return 0 + fi + echo " ⚠ upload attempt ${attempt}/${GITEE_UPLOAD_RETRIES} failed for ${fn}: $(printf '%s' "$response" | head -c 240)" >&2 + attempt=$((attempt + 1)) + [ "$attempt" -le "$GITEE_UPLOAD_RETRIES" ] && sleep "$GITEE_UPLOAD_RETRY_DELAY" + done + return 1 +} + +gitee_delete() { + curl -fsSL --connect-timeout "$GITEE_CURL_CONNECT_TIMEOUT" --max-time "$GITEE_CURL_MAX_TIME" \ + -X DELETE "${base}/releases/${release_id}/attach_files/${1}?access_token=${GITEE_TOKEN}" \ + >/dev/null 2>&1 || true +} + +uploaded=0 +replaced=0 +skipped=0 +failed=0 +for f in "$DIST_DIR"/dws-*.tar.gz "$DIST_DIR"/dws-*.zip "$DIST_DIR"/checksums.txt "$DIST_DIR"/dws-skills.zip; do + [ -f "$f" ] || continue + fn="$(basename "$f")" + local_sha="$(sha256_of "$f")" + ids="$(printf '%s\n' "$assets_map" | awk -F'\t' -v n="$fn" '$1==n {print $2}')" + aurl="$(printf '%s\n' "$assets_map" | awk -F'\t' -v n="$fn" '$1==n {print $3; exit}')" + count="$(printf '%s' "$ids" | grep -c . || true)" + + if [ "$count" -eq 1 ]; then + gitee_sha="$(api_get "$aurl" 2>/dev/null | sha256_of || true)" + if [ "$gitee_sha" = "$local_sha" ]; then + echo " ✓ ${fn} already correct on Gitee — skip" + skipped=$((skipped + 1)) + continue + fi + echo " ↻ ${fn} differs on Gitee — replacing" + elif [ "$count" -gt 1 ]; then + echo " ↻ ${fn} has ${count} copies on Gitee — replacing" + else + echo " ⬆ ${fn} (new)" + fi + + printf '%s\n' "$ids" | while read -r aid; do + [ -n "$aid" ] && gitee_delete "$aid" + done + if gitee_attach "$f"; then + if [ "$count" -eq 0 ]; then uploaded=$((uploaded + 1)); else replaced=$((replaced + 1)); fi + else + echo " ❌ upload failed for ${fn}" >&2 + failed=$((failed + 1)) + fi +done + +[ "$failed" -eq 0 ] || err "Gitee publish finished with ${failed} failed upload(s)" +echo "✅ Gitee release ${VERSION}: uploaded ${uploaded}, replaced ${replaced}, skipped ${skipped}" diff --git a/scripts/release/sync-to-gitee.sh b/scripts/release/sync-to-gitee.sh index 33db8948..7352593a 100755 --- a/scripts/release/sync-to-gitee.sh +++ b/scripts/release/sync-to-gitee.sh @@ -28,6 +28,11 @@ set -eu DIST_DIR="${DIST_DIR:-dist}" GITEE_API="${GITEE_API:-https://gitee.com/api/v5}" +GITEE_CURL_CONNECT_TIMEOUT="${GITEE_CURL_CONNECT_TIMEOUT:-15}" +GITEE_CURL_MAX_TIME="${GITEE_CURL_MAX_TIME:-120}" +GITEE_UPLOAD_MAX_TIME="${GITEE_UPLOAD_MAX_TIME:-300}" +GITEE_UPLOAD_RETRIES="${GITEE_UPLOAD_RETRIES:-3}" +GITEE_UPLOAD_RETRY_DELAY="${GITEE_UPLOAD_RETRY_DELAY:-10}" missing="" [ -z "${GITEE_TOKEN:-}" ] && missing="$missing GITEE_TOKEN" @@ -119,7 +124,7 @@ echo " Gitee release id = ${release_id}" # detail (/releases/{id}) endpoint: the latter's "assets" array omits the attach # id, so DELETE /attach_files/{id} was previously called with an empty id and # silently no-op'd — leaving stale + duplicate darwin binaries on Gitee. -assets_map="$(curl -fsSL "${base}/releases/${release_id}/attach_files?access_token=${GITEE_TOKEN}" 2>/dev/null \ +assets_map="$(curl -fsSL --connect-timeout "$GITEE_CURL_CONNECT_TIMEOUT" --max-time "$GITEE_CURL_MAX_TIME" "${base}/releases/${release_id}/attach_files?access_token=${GITEE_TOKEN}" 2>/dev/null \ | python3 -c 'import json,sys try: data=json.load(sys.stdin) @@ -137,13 +142,27 @@ sha256_of() { # sha256 of a file ($1) or, with no arg, of stdin } gitee_attach() { # upload file $1; success when the response carries a download url - printf '%s' "$(curl -fsSL -X POST "${base}/releases/${release_id}/attach_files" \ - -F "access_token=${GITEE_TOKEN}" -F "file=@${1}" 2>/dev/null || true)" \ - | grep -q '"browser_download_url"' + file="$1" + fn="$(basename "$file")" + attempt=1 + while [ "$attempt" -le "$GITEE_UPLOAD_RETRIES" ]; do + response="$(curl -fsS --connect-timeout "$GITEE_CURL_CONNECT_TIMEOUT" --max-time "$GITEE_UPLOAD_MAX_TIME" \ + --retry 2 --retry-delay 5 --retry-all-errors \ + -X POST "${base}/releases/${release_id}/attach_files" \ + -F "access_token=${GITEE_TOKEN}" -F "file=@${file}" 2>&1 || true)" + if printf '%s' "$response" | grep -q '"browser_download_url"'; then + return 0 + fi + echo " ⚠ upload attempt ${attempt}/${GITEE_UPLOAD_RETRIES} failed for ${fn}: $(printf '%s' "$response" | head -c 240)" >&2 + attempt=$((attempt + 1)) + [ "$attempt" -le "$GITEE_UPLOAD_RETRIES" ] && sleep "$GITEE_UPLOAD_RETRY_DELAY" + done + return 1 } gitee_delete() { # delete attachment by id $1 - curl -fsSL -X DELETE "${base}/releases/${release_id}/attach_files/${1}?access_token=${GITEE_TOKEN}" \ + curl -fsSL --connect-timeout "$GITEE_CURL_CONNECT_TIMEOUT" --max-time "$GITEE_CURL_MAX_TIME" \ + -X DELETE "${base}/releases/${release_id}/attach_files/${1}?access_token=${GITEE_TOKEN}" \ >/dev/null 2>&1 || true } @@ -166,7 +185,7 @@ for f in "$DIST_DIR"/dws-*.tar.gz "$DIST_DIR"/dws-*.zip "$DIST_DIR"/checksums.tx fi if [ "$count" -eq 1 ]; then - gitee_sha="$(curl -fsSL "$aurl" 2>/dev/null | sha256_of || true)" + gitee_sha="$(curl -fsSL --connect-timeout "$GITEE_CURL_CONNECT_TIMEOUT" --max-time "$GITEE_CURL_MAX_TIME" "$aurl" 2>/dev/null | sha256_of || true)" if [ "$gitee_sha" = "$local_sha" ]; then echo " ✓ ${fn} already correct on Gitee — skip" skipped=$((skipped + 1))