From b8e915edfb0306866894c7268b19bf8ec22d0efe 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 22:26:40 +0800 Subject: [PATCH 1/3] feat(install): auto-fallback to Gitee when GitHub unreachable (#492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Probe GitHub Releases on startup; if it is unreachable (typical in mainland China), automatically switch to the Gitee mirror — so a plain `curl … | sh` (or .ps1 / install-skills.sh) works everywhere with no DWS_GITEE_REPO env var. Explicit DWS_GITEE_REPO still wins; DWS_NO_FALLBACK=1 disables the probe; local source-checkout installs skip it. --- scripts/install-skills.sh | 11 +++++++++++ scripts/install.ps1 | 20 ++++++++++++++++++++ scripts/install.sh | 24 ++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/scripts/install-skills.sh b/scripts/install-skills.sh index cc4b32b8..d2ade351 100755 --- a/scripts/install-skills.sh +++ b/scripts/install-skills.sh @@ -19,6 +19,8 @@ set -eu REPO="DingTalk-Real-AI/dingtalk-workspace-cli" # China mirror: Gitee repo "owner/repo". When set, version + asset URLs resolve via Gitee API. GITEE_REPO="${DWS_GITEE_REPO:-}" +# Auto-fallback Gitee mirror used when GitHub is unreachable (see pick_source). +GITEE_FALLBACK_REPO="${DWS_GITEE_FALLBACK_REPO:-DingTalk-Real-AI/dingtalk-workspace-cli}" VERSION="${DWS_VERSION:-latest}" SKILL_NAME="dws" ROOT="${DWS_SKILLS_ROOT:-$PWD}" @@ -48,6 +50,14 @@ gitee_api() { return 1 } +pick_source() { + [ -n "$GITEE_REPO" ] && return 0 + [ "${DWS_NO_FALLBACK:-0}" = "1" ] && return 0 + curl -fsS --connect-timeout 5 --max-time 12 -o /dev/null "https://github.com/${REPO}/releases/latest" 2>/dev/null && return 0 + GITEE_REPO="$GITEE_FALLBACK_REPO" + printf ' ⚠ GitHub 不可达,自动切换国内 Gitee 镜像: %s\n' "$GITEE_REPO" +} + resolve_version() { if [ "$VERSION" = "latest" ]; then if [ -n "$GITEE_REPO" ]; then @@ -197,6 +207,7 @@ install_skills_to_root() { main() { need_cmd curl + pick_source resolve_version printf '\n' diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 5ae18efd..8e220ba8 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -29,6 +29,8 @@ $Repo = "DingTalk-Real-AI/dingtalk-workspace-cli" $BinName = "dws" # China mirror: Gitee repo "owner/repo". When set, version + asset URLs resolve via the Gitee API. $GiteeRepo = if ($env:DWS_GITEE_REPO) { $env:DWS_GITEE_REPO } else { "" } +# Auto-fallback Gitee mirror used when GitHub is unreachable (see Resolve-Source). +$GiteeFallbackRepo = if ($env:DWS_GITEE_FALLBACK_REPO) { $env:DWS_GITEE_FALLBACK_REPO } else { "DingTalk-Real-AI/dingtalk-workspace-cli" } $InstallDir = if ($env:DWS_INSTALL_DIR) { $env:DWS_INSTALL_DIR } else { Join-Path $HOME ".local\bin" } $Version = if ($env:DWS_VERSION) { $env:DWS_VERSION } else { "latest" } $NoSkills = $env:DWS_NO_SKILLS -eq "1" @@ -146,6 +148,20 @@ function Get-GiteeAssetUrl { return "" } +function Resolve-Source { + # Explicit DWS_GITEE_REPO wins; else probe GitHub and fall back to Gitee when unreachable. + if ($GiteeRepo -ne "") { return } + if ($env:DWS_NO_FALLBACK -eq "1") { return } + try { + Invoke-WebRequest -Uri "https://github.com/$Repo/releases/latest" -Method Head ` + -TimeoutSec 12 -UseBasicParsing -ErrorAction Stop 2>$null | Out-Null + return + } catch { + $script:GiteeRepo = $GiteeFallbackRepo + Write-Say "⚠ GitHub 不可达,自动切换国内 Gitee 镜像: $script:GiteeRepo" + } +} + function Resolve-LatestVersion { if ($Version -eq "latest") { if ($GiteeRepo -ne "") { @@ -591,6 +607,10 @@ $SourceRoot = Resolve-SourceRoot Write-Banner +# Pick GitHub vs Gitee mirror (auto-fallback when GitHub is unreachable). +# Skipped when installing from a local source checkout (no download needed). +if (-not $SourceRoot) { Resolve-Source } + if (!$NoSkills) { Resolve-SkillMode } diff --git a/scripts/install.sh b/scripts/install.sh index 7406c100..5d6f4cb7 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -28,6 +28,11 @@ BIN_NAME="dws" # the Gitee API (https://gitee.com/api/v5) instead of GitHub. Gitee attachment URLs # carry an unstable numeric id, so every asset is resolved by name at install time. GITEE_REPO="${DWS_GITEE_REPO:-}" +# Auto-fallback: when DWS_GITEE_REPO is not set, the installer probes GitHub and, +# if it is unreachable (typical in mainland China), automatically switches to this +# Gitee mirror — so a plain `curl … | sh` works everywhere with no env var. +# Set DWS_NO_FALLBACK=1 to disable the probe and force GitHub. +GITEE_FALLBACK_REPO="${DWS_GITEE_FALLBACK_REPO:-DingTalk-Real-AI/dingtalk-workspace-cli}" INSTALL_DIR="${DWS_INSTALL_DIR:-$HOME/.local/bin}" INSTALL_NAME="${DWS_INSTALL_NAME:-$BIN_NAME}" VERSION="${DWS_VERSION:-latest}" @@ -153,6 +158,21 @@ detect_arch() { esac } +# Decide the download source. An explicit DWS_GITEE_REPO always wins. Otherwise +# probe GitHub Releases; if it is unreachable (typical in mainland China), switch +# GITEE_REPO to the mirror so every subsequent resolve/download uses Gitee. +pick_source() { + [ -n "$GITEE_REPO" ] && return 0 + [ "${DWS_NO_FALLBACK:-0}" = "1" ] && return 0 + if need_cmd curl; then + curl -fsS --connect-timeout 5 --max-time 12 -o /dev/null "https://github.com/${REPO}/releases/latest" 2>/dev/null && return 0 + elif need_cmd wget; then + wget -q --timeout=12 --tries=1 -O /dev/null "https://github.com/${REPO}/releases/latest" 2>/dev/null && return 0 + fi + GITEE_REPO="$GITEE_FALLBACK_REPO" + say "⚠ GitHub 不可达,自动切换国内 Gitee 镜像: ${GITEE_REPO}" +} + # Resolve the latest version tag from GitHub resolve_version() { if [ "$VERSION" = "latest" ]; then @@ -613,6 +633,10 @@ main() { print_banner + # Pick GitHub vs Gitee mirror (auto-fallback when GitHub is unreachable). + # Skipped when installing from a local source checkout (no download needed). + [ -z "$source_root" ] && pick_source + # Resolve skill mode only when we are actually going to touch skills. if [ "$NO_SKILLS" != "1" ]; then resolve_skill_mode From 9a8157a4cd1ef04641ed0c068d7302ae93a30262 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 22:26:47 +0800 Subject: [PATCH 2/3] ci(mirror): auto-push code to Gitee via HTTPS token (no SSH key) (#493) Rewrite mirror-to-gitee.yml to push main + tags to the Gitee mirror using HTTPS + GITEE_TOKEN (reusing the existing secret) instead of hub-mirror-action + an SSH key. Gated on GITEE_TOKEN; needs GITEE_USER + GITEE_REPO secrets. Keeps Gitee code/raw in sync automatically on every push to main + tags, so no more manual git push after each release. --- .github/workflows/mirror-to-gitee.yml | 45 +++++++++++++++------------ 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/.github/workflows/mirror-to-gitee.yml b/.github/workflows/mirror-to-gitee.yml index a366abc5..022515cc 100644 --- a/.github/workflows/mirror-to-gitee.yml +++ b/.github/workflows/mirror-to-gitee.yml @@ -1,8 +1,9 @@ -# 此 workflow 把本仓库代码自动镜像到 Gitee,供国内用户访问。 -# 需要在仓库 Settings → Secrets and variables → Actions 配置两个 secret: -# GITEE_TOKEN —— Gitee 私人令牌(在 Gitee 设置→私人令牌生成),需勾选 projects + admin:repo_hook 权限。 -# GITEE_PRIVATE_KEY —— SSH 私钥(本地 ssh-keygen 生成的私钥),对应公钥需添加到 Gitee 账号的 SSH 公钥中。 -# 未配置 secret 时(例如 fork 仓库),mirror step 会被自动跳过,不会报错(不会出现红叉)。 +# 把本仓库代码自动镜像到 Gitee,供国内用户访问(raw 脚本入口 + tags)。 +# 用 HTTPS + 令牌直接 git push(无需 SSH key),复用已配置的 secret: +# GITEE_TOKEN —— Gitee 私人令牌(勾 projects) +# GITEE_USER —— 令牌所属 Gitee 用户名(用于 https 推送鉴权) +# GITEE_REPO —— "owner/repo",如 DingTalk-Real-AI/dingtalk-workspace-cli +# 未配置 GITEE_TOKEN 时(如 fork)自动跳过,不报红叉。 name: Mirror code to Gitee on: @@ -18,22 +19,26 @@ on: jobs: mirror: runs-on: ubuntu-latest - # GitHub Actions 不允许在 job-level if 直接引用 secrets,因此先用 env 暴露 secret, - # 再在 step 里做守卫判断。hub-mirror 必须有 SSH 私钥(GITEE_PRIVATE_KEY)才能推送, - # 故以它为守卫:未配置时整步跳过(代码镜像可改由 Gitee 侧 pull-mirror 承担),不报红叉。 + # GitHub Actions 不允许在 job-level if 直接引用 secrets,故先用 env 暴露再在 step 守卫。 env: GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }} - GITEE_PRIVATE_KEY: ${{ secrets.GITEE_PRIVATE_KEY }} + GITEE_USER: ${{ secrets.GITEE_USER }} + GITEE_REPO: ${{ secrets.GITEE_REPO }} steps: - - name: Mirror to Gitee - if: env.GITEE_PRIVATE_KEY != '' - uses: Yikun/hub-mirror-action@master + - name: Checkout (full history + tags) + if: env.GITEE_TOKEN != '' + uses: actions/checkout@v4 with: - src: "github/DingTalk-Real-AI" - dst: "gitee/DingTalk-Real-AI" - dst_key: ${{ secrets.GITEE_PRIVATE_KEY }} - dst_token: ${{ secrets.GITEE_TOKEN }} - account_type: "org" - static_list: "dingtalk-workspace-cli" - clone_style: "https" - force_update: true + fetch-depth: 0 + + - name: Push main + tags to Gitee + if: env.GITEE_TOKEN != '' + run: | + set -eu + REMOTE="https://${GITEE_USER}:${GITEE_TOKEN}@gitee.com/${GITEE_REPO}.git" + # 取到 main 与所有 tag(落到 origin/* 与本地 tags,避免推当前分支引用冲突) + git fetch --force --tags origin 'refs/heads/main:refs/remotes/origin/main' + # 镜像对齐(force:Gitee 始终跟随 GitHub) + git push --force "$REMOTE" 'refs/remotes/origin/main:refs/heads/main' + git push --force --tags "$REMOTE" + echo "✅ 已镜像 main + tags 到 Gitee ${GITEE_REPO}" From 0de6e46ef4fc992787e7367dabbb0b5b3d6ab6da 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 22:26:54 +0800 Subject: [PATCH 3/3] docs: cut 1.0.41 changelog + backfill 1.0.40 #488 (#495) - 1.0.41: install auto-fallback to Gitee (#492) + CI auto-push mirror (#493) - 1.0.40: add the missing #488 (skills embedded in binary) entry --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2de85f5d..c5300ebf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ The format is inspired by [Keep a Changelog](https://keepachangelog.com/) and th ## [Unreleased] +## [1.0.41] - 2026-06-24 + +This release makes the installers work from mainland China out of the box (no env var) and keeps the Gitee mirror in sync automatically. + +### Added + +- **Auto-fallback to the Gitee mirror when GitHub is unreachable** (#492; `scripts/install.sh`, `scripts/install.ps1`, `scripts/install-skills.sh`) — the installers probe GitHub Releases on startup and, when it is unreachable (typical in mainland China), automatically resolve the version and download every asset (binary, `checksums.txt`, `dws-skills.zip`) from the Gitee mirror instead. A plain `curl … | sh` now works in China with no `DWS_GITEE_REPO` needed. Explicit `DWS_GITEE_REPO` still wins, `DWS_NO_FALLBACK=1` forces GitHub, and local source-checkout installs skip the probe. + +### Changed + +- **CI mirrors repo code to Gitee automatically** (#493; `.github/workflows/mirror-to-gitee.yml`) — the mirror workflow now pushes `main` + tags to the Gitee mirror over HTTPS using `GITEE_TOKEN` (no SSH key), on every push to `main` and every tag, keeping the Gitee `raw/main` install scripts and tags in sync without any manual `git push`. Gated on `GITEE_TOKEN`; skips cleanly when unset. + ## [1.0.40] - 2026-06-24 This release adds China-accessible install mirrors so the CLI installs reliably from mainland China, where GitHub raw + Releases are slow or fail. @@ -13,6 +25,7 @@ This release adds China-accessible install mirrors so the CLI installs reliably ### Added - **China mirror via Gitee + npmmirror** (#486; `scripts/install.sh`, `scripts/install.ps1`, `scripts/install-skills.sh`, `scripts/release/sync-to-gitee.sh`, `.github/workflows/release.yml`, `.github/workflows/mirror-to-gitee.yml`) — an opt-in `DWS_GITEE_REPO` env var makes all three installers resolve the latest version and every release asset (binary, `checksums.txt`, `dws-skills.zip`) from the Gitee OpenAPI v5 instead of GitHub; with it unset, installation defaults to GitHub (fully backward compatible). The release pipeline mirrors release attachments to the matching Gitee release after each tag (gated on `GITEE_TOKEN`/`GITEE_REPO`), and a hub-mirror workflow keeps the repo code in sync (gated on `GITEE_PRIVATE_KEY`). README documents three China install channels: Gitee raw script, Gitee release binaries, and the npm package via `registry.npmmirror.com`. +- **Skills embedded in the binary** (#488; `skills_embed.go`, `internal/app/skill_setup.go`, `internal/app/skill_setup_embed.go`) — the `skills/` tree (mono + multi) is embedded into the `dws` binary via `go:embed` and `dws skill setup` defaults to the embedded copy, refreshing the installed skill instead of silently reusing a stale copy probed from the current working directory — so skills install offline with no separate download. ## [1.0.39] - 2026-06-18