From 430d20f2ee3d692c65979df25fb60d5cc057eb4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=BF=AE=E9=9B=A8?=
<47820304+PeterGuy326@users.noreply.github.com>
Date: Wed, 24 Jun 2026 11:43:06 +0800
Subject: [PATCH] ci(release): auto-sync release artifacts to China OSS mirror
Add scripts/release/sync-to-oss.sh and wire it into release.yml. After
the GitHub release, artifacts (binaries, checksums, dws-skills.zip) plus
the install scripts are pushed to an OSS bucket in the
// layout the install.sh DWS_RELEASE_BASE switch
expects, with a latest.txt pointer.
Gated on OSS_* repo secrets: skips cleanly when unset, so forks without
the secrets keep releasing to GitHub only.
---
.github/workflows/release.yml | 12 ++++
scripts/release/sync-to-oss.sh | 100 +++++++++++++++++++++++++++++++++
2 files changed, 112 insertions(+)
create mode 100755 scripts/release/sync-to-oss.sh
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index df68a5bb..26a3c867 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -58,6 +58,18 @@ jobs:
run: |
gh release upload "${{ github.ref_name }}" dist/dws-skills.zip --clobber
+ - name: Sync release to China OSS mirror
+ # 自动同步到国内镜像,供 install.sh 的 DWS_RELEASE_BASE 开关消费。
+ # 脚本自带门控:未配置 OSS_* secret 时优雅跳过,不影响海外发布。
+ run: ./scripts/release/sync-to-oss.sh
+ env:
+ VERSION: ${{ github.ref_name }}
+ OSS_ACCESS_KEY_ID: ${{ secrets.OSS_ACCESS_KEY_ID }}
+ OSS_ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
+ OSS_ENDPOINT: ${{ secrets.OSS_ENDPOINT }}
+ OSS_BUCKET: ${{ secrets.OSS_BUCKET }}
+ OSS_PREFIX: ${{ secrets.OSS_PREFIX }}
+
- name: Setup Node.js
uses: actions/setup-node@v4
with:
diff --git a/scripts/release/sync-to-oss.sh b/scripts/release/sync-to-oss.sh
new file mode 100755
index 00000000..54a7332d
--- /dev/null
+++ b/scripts/release/sync-to-oss.sh
@@ -0,0 +1,100 @@
+#!/usr/bin/env bash
+# Copyright 2026 Alibaba Group
+# Licensed under the Apache License, Version 2.0
+#
+# Sync release artifacts to a China-accessible OSS mirror so that the install
+# scripts' DWS_RELEASE_BASE switch can serve domestic users.
+#
+# Mirror layout produced (matches install.sh RELEASE_BASE="//"):
+# oss://$OSS_BUCKET/$OSS_PREFIX/download//dws--.tar.gz|.zip
+# oss://$OSS_BUCKET/$OSS_PREFIX/download//checksums.txt
+# oss://$OSS_BUCKET/$OSS_PREFIX/download//dws-skills.zip
+# oss://$OSS_BUCKET/$OSS_PREFIX/latest.txt (plain version string)
+#
+# So with the CDN/custom domain CNAME'd to the bucket, China users run:
+# DWS_RELEASE_BASE=https:///$OSS_PREFIX/download \
+# DWS_VERSION= \
+# curl -fsSL https:///$OSS_PREFIX/install.sh | sh
+#
+# Required environment (CI secrets):
+# OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET, OSS_ENDPOINT, OSS_BUCKET
+# Optional:
+# OSS_PREFIX (default: dws)
+# DIST_DIR (default: ./dist)
+# VERSION (default: derived from `git describe`)
+#
+# Gating: if the required OSS_* vars are unset, the script exits 0 with a notice
+# (so it can be wired into release.yml without breaking forks that lack secrets).
+
+set -eu
+
+DIST_DIR="${DIST_DIR:-dist}"
+OSS_PREFIX="${OSS_PREFIX:-dws}"
+
+# ── Gate: skip cleanly when credentials are not configured ────────────────────
+missing=""
+for v in OSS_ACCESS_KEY_ID OSS_ACCESS_KEY_SECRET OSS_ENDPOINT OSS_BUCKET; do
+ eval "val=\${$v:-}"
+ [ -z "$val" ] && missing="$missing $v"
+done
+if [ -n "$missing" ]; then
+ echo "ℹ️ OSS mirror sync skipped — missing:${missing}"
+ echo " Set these as repo secrets to auto-publish releases to the China mirror."
+ exit 0
+fi
+
+# ── Resolve version ──────────────────────────────────────────────────────────
+VERSION="${VERSION:-$(git describe --tags --always 2>/dev/null || echo dev)}"
+echo "📦 Syncing release ${VERSION} → oss://${OSS_BUCKET}/${OSS_PREFIX}/download/${VERSION}/"
+
+# ── Ensure ossutil is available ───────────────────────────────────────────────
+OSSUTIL="${OSSUTIL:-ossutil}"
+if ! command -v "$OSSUTIL" >/dev/null 2>&1; then
+ echo "⬇ ossutil not found; installing to ./.ossutil ..."
+ os="$(uname -s | tr '[:upper:]' '[:lower:]')"
+ arch="$(uname -m)"
+ case "$arch" in x86_64|amd64) arch=amd64 ;; aarch64|arm64) arch=arm64 ;; esac
+ curl -fsSL "https://gosspublic.alicdn.com/ossutil/v2/2.0.0/ossutil-2.0.0-${os}-${arch}.zip" -o /tmp/ossutil.zip
+ unzip -qo /tmp/ossutil.zip -d /tmp/ossutil-extract
+ found="$(find /tmp/ossutil-extract -name ossutil -type f | head -1)"
+ mkdir -p ./.ossutil && cp "$found" ./.ossutil/ossutil && chmod +x ./.ossutil/ossutil
+ OSSUTIL="./.ossutil/ossutil"
+fi
+
+oss_cp() {
+ # oss_cp
+ "$OSSUTIL" cp -f \
+ --access-key-id "$OSS_ACCESS_KEY_ID" \
+ --access-key-secret "$OSS_ACCESS_KEY_SECRET" \
+ --endpoint "$OSS_ENDPOINT" \
+ "$1" "oss://${OSS_BUCKET}/$2"
+}
+
+base="${OSS_PREFIX}/download/${VERSION}"
+
+# ── Upload binaries + checksums + skills ──────────────────────────────────────
+uploaded=0
+for f in "$DIST_DIR"/dws-*.tar.gz "$DIST_DIR"/dws-*.zip "$DIST_DIR"/checksums.txt; do
+ [ -f "$f" ] || continue
+ oss_cp "$f" "${base}/$(basename "$f")"
+ uploaded=$((uploaded + 1))
+done
+
+if [ "$uploaded" -eq 0 ]; then
+ echo "❌ No artifacts found in ${DIST_DIR}. Run the build (goreleaser) first." >&2
+ exit 1
+fi
+
+# ── Publish the latest pointer (for DWS_VERSION=latest resolution on mirror) ──
+echo "$VERSION" > /tmp/dws-latest.txt
+oss_cp /tmp/dws-latest.txt "${OSS_PREFIX}/latest.txt"
+
+# ── Publish the install scripts themselves (entry layer) ──────────────────────
+for s in install.sh install.ps1 install-skills.sh; do
+ [ -f "scripts/$s" ] && oss_cp "scripts/$s" "${OSS_PREFIX}/$s"
+done
+
+echo "✅ Synced ${uploaded} artifact(s) + install scripts + latest.txt to the OSS mirror."
+echo " China install:"
+echo " DWS_RELEASE_BASE=https:///${OSS_PREFIX}/download DWS_VERSION=${VERSION} \\"
+echo " curl -fsSL https:///${OSS_PREFIX}/install.sh | sh"