* fix release upload of signed macOS assets
* ci: sign macOS releases with Developer ID
* fix release publication atomicity
* harden Developer ID release verification
* fix: run release script tests in CI
---------
Co-authored-by: 玉澜 <yulan.wqy@alibaba-inc.com>
* feat(auth): support multi-profile login
* fix(auth): complete multi-org profile acceptance
* feat(auth): 完成多组织 profile 验收
* docs(auth): 补充多组织 Ralph 验收材料
* feat(auth): 支持 auth switch TUI 切换 profile
* feat(auth): logout 默认清理所有组织
* feat(auth): login 默认新增组织授权
* feat(profile): 使用 profile switch 切换组织
* docs(ralph): 更新 profile switch 验收材料
* fix(profile): 展示全部可切换组织
* feat(profile): support multi-org switch tui
* chore(install): add branch source installer
* fix(profile): keep global profile out of tool params
* feat(profile): support csv multi-profile runtime
* ci: add multi-profile e2e workflow
* ci: run multi-profile e2e on all branches
* docs: document multi-profile e2e ci gate
* docs: remove multi-profile test cases from pr
* ci: harden multi-profile e2e gates
* fix(auth): serialize profiles.json RMW and harden multi-profile persistence
Wrap all profiles.json read-modify-write paths (profile switch/use/remove,
status marking, token save, logout) in the existing dual-layer lock via a new
withProfilesLock helper. Split each writer into a public (locking) entry point
plus a lock-free *Locked variant so the non-reentrant lock is never re-acquired;
the refresh path (oauth_helpers) and the load-path legacy migration now call the
lock-free saver to avoid self-deadlock.
Also: write profiles.json and the token marker via per-write random temp names
(uuid) to stop concurrent writers from corrupting a fixed .tmp file; quarantine
an unparseable profiles.json and rebuild an empty config so the CLI can
self-heal instead of locking out auth reset/logout; make DeleteAllTokenData
proceed even if profiles.json cannot be read; and stop SyncLegacyTokenMirror
from deleting the legacy mirror on a transient keychain read error.
* fix(auth): do not fall back to a different org's legacy token slot
When no explicit --profile is given, LoadTokenDataForProfile resolves the
current/primary profile and reads its per-corp keychain slot. If that slot
read failed, the code silently fell through to the legacy single token slot,
which after any drift between the legacy mirror and the current profile could
belong to a different organization. The command would then run as the wrong
org with no indication to the user.
Reproduction (conceptual):
- profiles.json currentProfile = corpA
- corpA's keychain slot is unreadable, legacy single slot still holds corpB
- any read command (no --profile) silently used corpB's token
Fix: when a profile is resolved but its slot read fails and no --profile was
given, only fall back to the legacy single slot when its CorpID matches the
resolved profile (same org); otherwise return the original error instead of
acting as a different organization. The no-profile legacy path (pre-migration
installs with no resolved profile) is unchanged.
Tests:
- Covered by the existing internal/auth suite under go test -race; the
same-org fallback preserves the legacy-mirror case while the cross-org
case now surfaces the read error.
* feat(skill): document multi-org profile usage and always ship dws-shared
The skills had no guidance on the multi-profile capability, so an agent would
treat the CLI as single-org: when a lookup missed in the current org it would
give up or ask the user instead of searching other logged-in orgs. The multi
skill set also referenced a `dws-shared` prerequisite that was never actually
installed, and the only multi-org hints lived inline in three product skills.
This adds, in source only:
- A "multi-org / profile" section in the mono SKILL.md (concept, commands,
cross-org rule, aggregation, safety guardrails) plus a decision-tree entry,
trigger conditions, and a corrected logout danger-table row (logout removes
all orgs by default; removing the primary silently re-elects a new primary,
confirm before removing the primary).
- A standalone skills/multi/dingtalk-profile skill mirroring the same content.
- A new skills/multi/dws-shared skill that carries auth, global flags and the
multi-org rule, so every product skill's PREREQUISITE resolves and all
read/search skills inherit the cross-org behavior without per-skill edits.
- Cross-org fallback notes on dingtalk-aisearch / chat / contact.
To guarantee the prerequisite actually ships, multi-mode install now force-
includes dws-shared even when --skill / --exclude narrows the set (no-op when
the source has no dws-shared, preserving older layouts).
Tests:
- internal/app: TestP1SharedAlwaysIncludedWithSkillFilter installs with
`-s aitable` and asserts dws-shared still lands in the destination;
TestP1SharedNoopWhenAbsent guards the older-layout no-op.
- go test -race ./internal/auth/... ./internal/app/... passes.
---------
Co-authored-by: shangguanxuan.sgx <shangguanxuan.sgx@alibaba-inc.com>
Co-authored-by: qinze <audanye@gmail.com>
The Gitee mirror force-pushes main verbatim, so Chinese users saw a
README whose top install commands point at raw.githubusercontent.com
(hard to reach in China) and a coverage badge that fails to render
(the relative .github/badges/coverage.svg can't be served by Gitee —
gitee raw returns a signed, expiring URL with content-type text/plain).
Add a Gitee-only post-process step: build a gitee-main branch on top of
origin/main and rewrite README.md / README_zh.md before pushing —
(1) raw.githubusercontent.com/<repo>/main -> gitee.com/<repo>/raw/main
(2) the coverage badge -> a shields.io static badge whose percentage is
read from the repo's coverage.svg and colored by threshold.
GitHub's README is untouched; only the Gitee copy is rewritten. The
branch is rebuilt from origin/main every run, so it stays a clean
single-commit delta and never drifts.
Two leftovers referenced the decommissioned wxianfeng fork branch
`feat/dws-devapp`, both now obsolete after the dev-app work landed on main
(v1.0.42) and the installers were repointed to DingTalk-Real-AI (#505):
- .github/workflows/auto-dev-release.yml — triggered only on push to
feat/dws-devapp (a branch that does not exist on this repo, so it never
fires). Its purpose — auto-publishing fork dev-preview releases for
install-devapp.sh — is gone now that install-devapp.sh pulls stable
releases from DingTalk-Real-AI.
- docs/devapp-yulan-command-routing.md — a 2026-06-05 draft design doc
pinned to the fork branch and the pre-rename `devapp` command tree,
superseded by the shipped `dws dev` command set and the rewritten
docs/devapp-agent-install-guide.md.
After this, the repo has zero `wxianfeng` / `feat/dws-devapp` references.
The v1.0.42 Release job hit timeout-minutes: 30 mid-upload while mirroring
release assets to Gitee, so the Gitee release ended up missing
dws-windows-arm64.zip and checksums.txt. Root cause + fixes:
- sync-to-gitee.sh now skips assets already attached to the Gitee release,
so a re-run only uploads what is missing (instead of re-uploading every
artifact and creating duplicates). It no longer fails when everything is
already present.
- New workflow sync-release-to-gitee.yml (workflow_dispatch, version input)
mirrors a published GitHub release's assets to Gitee on its own — it
downloads the assets from the GitHub release and runs the idempotent sync,
without running GoReleaser or touching the GitHub release (no outage). Use
it to repair an incomplete Gitee mirror.
- Bump the Release job timeout 30 -> 60 so a full Gitee upload has room.
bash -n + YAML validated.
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.
- CHANGELOG: 1.0.40 (China mirror via Gitee + npmmirror, #486)
- mirror-to-gitee.yml: gate on GITEE_PRIVATE_KEY so it skips cleanly when the
SSH key is unset (code mirror handled by Gitee-side pull-mirror) instead of
failing the run.
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
<base>/<version>/<file> 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.
GoReleaser cross-compiles darwin/arm64 binaries on ubuntu-latest with no
codesign step. macOS 11+ on Apple Silicon requires at least an ad-hoc
signature; unsigned arm64 binaries are SIGKILL'd by amfid on first exec,
which surfaces as `signal: killed` and aborts `dws upgrade` at the
"解压并验证" step.
Two layers of fix:
1. Release-side: post-goreleaser.sh now unpacks each dws-darwin-*.tar.gz,
applies an ad-hoc signature (codesign locally, rcodesign in CI),
deterministically repacks, and rewrites the matching line in
checksums.txt. release.yml installs rcodesign 0.27.0 before
GoReleaser runs.
2. Client-side self-heal: validateNewBinary detects `signal: killed` on
darwin and retries once after running `codesign --force --sign -` and
clearing com.apple.quarantine. Future releases stay functional even
if the signing step is ever skipped.
Verified end-to-end: stripped a real dws binary → exec exits 137 →
validateNewBinary recovers → final binary shows Signature=adhoc and runs.
GitHub occasionally drops tag push events; this adds a manual trigger
so we can re-run the release job against any tag ref without having to
delete and re-push the tag.