Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e15115ad4 | ||
|
|
25bf3d12f2 | ||
|
|
f78cc5c846 | ||
|
|
72fe795f3f |
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is inspired by [Keep a Changelog](https://keepachangelog.com/) and this project follows [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [1.0.23] - 2026-05-08
|
||||
|
||||
A single fix for HTTP proxy support across the CLI's custom HTTP transports. No behaviour changes elsewhere.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`HTTP_PROXY` / `HTTPS_PROXY` environment variables silently ignored by all custom transports** (#237, fixes #236) — the three custom `http.Transport` instances built by the CLI (`internal/transport/client.go` MCP transport, `internal/apiclient/client.go` DingTalk OpenAPI client, `internal/app/legacy.go` IPv4-forcing registry client) all set `DialContext` / `TLSClientConfig` / timeouts but omitted the `Proxy` field. Per Go's `net/http` contract, a non-nil Transport without an explicit `Proxy` means "no proxy" — env vars are silently ignored, breaking sandboxed or air-gapped deployments that route outbound through `HTTP_PROXY` / `HTTPS_PROXY`. All three transports now set `Proxy: http.ProxyFromEnvironment`.
|
||||
|
||||
### Tests
|
||||
|
||||
- Per-package regression test that pointer-compares the Transport's `Proxy` func against `http.ProxyFromEnvironment`, avoiding flakiness from Go's `envProxyOnce` memoisation when running alongside tests that read proxy env early. (#237)
|
||||
|
||||
## [1.0.22] - 2026-05-07
|
||||
|
||||
Two release-blocking bug fixes: `dws attendance summary` now exposes the server-required `--stats-type` flag (without it, every call returned C0002), and the install scripts finally populate `~/.hermes/skills/dws/` for users who already have Hermes.
|
||||
|
||||
@@ -68,16 +68,21 @@ func newAuthLoginCommand() *cobra.Command {
|
||||
Long: `登录钉钉并获取认证凭证。
|
||||
|
||||
支持的登录方式:
|
||||
- OAuth 设备流 (默认): 通过钉钉扫码授权登录
|
||||
- 直接提供 Token: 通过 --token 参数传入已有 token
|
||||
- OAuth Loopback 流 (默认): 本机自动起 127.0.0.1 监听接收回调,浏览器授权后自动完成
|
||||
- OAuth 设备流 (--device): 显示 user_code + 短 URL,适合 SSH 远程 / 容器 / 无头环境
|
||||
- 直接提供 Token (--token): 跳过授权,使用已有 token
|
||||
|
||||
不支持的登录方式:
|
||||
- 邮箱/密码登录
|
||||
- 手机号/验证码登录
|
||||
- 应用凭证 (AppKey/AppSecret) 直接登录
|
||||
|
||||
注意: SSH 远程或无头环境(无本地浏览器可访问远端的 127.0.0.1)请使用 --device,
|
||||
否则 OAuth 回调会跳到本机不可达的 127.0.0.1 链接,授权完成后无法回写 token。
|
||||
|
||||
示例:
|
||||
dws auth login # 扫码登录
|
||||
dws auth login # 本机扫码登录 (loopback 流)
|
||||
dws auth login --device # SSH 远程 / 无头环境登录 (设备流)
|
||||
dws auth login --force # 强制重新登录 (忽略缓存 token)
|
||||
dws auth login --token xxx # 使用指定 token`,
|
||||
DisableAutoGenTag: true,
|
||||
|
||||
@@ -120,8 +120,8 @@ func flagErrorWithSuggestions(cmd *cobra.Command, err error) error {
|
||||
// Common flag aliases and suggestions
|
||||
suggestions := map[string]string{
|
||||
"--json": "提示: 请使用 --format json 或 -f json 来输出 JSON 格式",
|
||||
"--method": "提示: dws auth login 默认使用 OAuth 设备流登录,无需指定 --method",
|
||||
"--device-flow": "提示: dws auth login 默认已使用设备流,无需 --device-flow 参数",
|
||||
"--method": "提示: dws auth login 默认使用 OAuth loopback 流;SSH/无头环境请加 --device 走设备流",
|
||||
"--device-flow": "提示: 设备流的标志名是 --device(不是 --device-flow),SSH/无头环境登录请用 dws auth login --device",
|
||||
"--email": "提示: dws 不支持邮箱/密码登录,请使用 dws auth login 进行扫码登录",
|
||||
"--code": "提示: dws 不支持验证码登录,请使用 dws auth login 进行扫码登录",
|
||||
"--corp-id": "提示: corp-id 会在登录时自动获取,无需手动指定",
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/upgrade"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -57,6 +58,14 @@ func newUpgradeCommand() *cobra.Command {
|
||||
dws upgrade -y # 跳过确认直接升级`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if h := edition.Get(); h != nil && h.IsEmbedded {
|
||||
name := h.Name
|
||||
if name == "" {
|
||||
name = "embedded"
|
||||
}
|
||||
return fmt.Errorf("当前运行在嵌入模式(%s),dws upgrade 已禁用;请通过宿主完成升级", name)
|
||||
}
|
||||
|
||||
yes, _ := cmd.Flags().GetBool("yes")
|
||||
format := resolveUpgradeFormat(cmd)
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
)
|
||||
|
||||
func TestUpgradeCommand_BlockedInEmbeddedMode(t *testing.T) {
|
||||
prev := edition.Get()
|
||||
edition.Override(&edition.Hooks{IsEmbedded: true, Name: "embedded"})
|
||||
t.Cleanup(func() { edition.Override(prev) })
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
args []string
|
||||
}{
|
||||
{"check", []string{"--check"}},
|
||||
{"list", []string{"--list"}},
|
||||
{"rollback", []string{"--rollback"}},
|
||||
{"plain", []string{}},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := newUpgradeCommand()
|
||||
var out, errBuf bytes.Buffer
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&errBuf)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Fatalf("upgrade %v in embedded mode must return error, got nil", tc.args)
|
||||
}
|
||||
msg := err.Error()
|
||||
if !strings.Contains(msg, "嵌入模式") {
|
||||
t.Errorf("error message should mention 嵌入模式, got: %q", msg)
|
||||
}
|
||||
if !strings.Contains(msg, "embedded") {
|
||||
t.Errorf("error message should include edition name, got: %q", msg)
|
||||
}
|
||||
if !strings.Contains(msg, "dws upgrade") {
|
||||
t.Errorf("error message should reference dws upgrade for clarity, got: %q", msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpgradeCommand_NotBlockedInOpenSourceMode(t *testing.T) {
|
||||
prev := edition.Get()
|
||||
edition.Override(&edition.Hooks{IsEmbedded: false, Name: "open"})
|
||||
t.Cleanup(func() { edition.Override(prev) })
|
||||
|
||||
cmd := newUpgradeCommand()
|
||||
var out, errBuf bytes.Buffer
|
||||
cmd.SetOut(&out)
|
||||
cmd.SetErr(&errBuf)
|
||||
cmd.SetArgs([]string{"--check"})
|
||||
|
||||
err := cmd.Execute()
|
||||
if err != nil && strings.Contains(err.Error(), "嵌入模式") {
|
||||
t.Errorf("open-source mode must not be blocked by embedded guard, got: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user