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}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 68e7e853..28cd8a9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,18 @@ The format is inspired by [Keep a Changelog](https://keepachangelog.com/) and th - **`robot connect` now hints how to match terminal answer quality** (issue #39; `internal/helpers/devapp_connect.go`) — when neither a work dir nor a knowledge source is configured, the connector prints a one-time note that the bot runs in a clean temp dir without local project context, pointing at `--agent-workdir` / `--knowledge-dir` / `--knowledge-source` / `--agent-model`. The robot quickstart gains matching FAQ entries, plus a clarification that step 3 (`robot connect`) produces no approval ticket (issue #19). +## [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. @@ -21,6 +33,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 diff --git a/scripts/install-skills.sh b/scripts/install-skills.sh index 49ab20bb..5ff18b16 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 @@ -199,6 +209,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 6ee676e3..ee75e527 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" @@ -148,6 +150,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 "") { @@ -593,6 +609,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 f087ecb6..c981d3e9 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 @@ -615,6 +635,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