The fragment gate only ran validation when the changed path matched the
legal fragment name pattern, so `.changes/Foo.md`, `.changes/notes.txt`
and a symlinked fragment slipped through untouched and then broke the
next PR that added a legal fragment. The trigger now fires on any
top-level `.changes/` change other than README.md and rejects every
entry that is not README.md, released/, or a 100644 blob named
^[a-z0-9][a-z0-9._-]*\.md$.
The renderer had the same hole from the other side: `find -type f`
is false for symlinks, so a symlinked fragment was silently dropped
from the rendered notes, and the `[a-z0-9]*.md` glob only constrained
the first character so `chat reply.md` passed. It now walks every
top-level entry and fails on symlinks, unexpected directories,
non-regular files and illegal names. Both scripts pin LC_ALL=C so the
ASCII ranges cannot match uppercase under a different collation.
Adds regression coverage for illegal names, non-markdown entries,
symlinks and executable modes on both the gate and the renderer.
Gitee API returns HTTP 200 with null body when a release tag doesn't
exist, unlike GitHub which returns 404. Treat empty release_id as
"no release exists" instead of erroring out.
Stable releases previously required a byte-identical tree with the
promoted beta (only CHANGELOG.md could differ) and local releases had
to run exactly at the origin/main tip with an atomic main+tag push.
Together these froze main for the whole beta-to-stable window.
Relax both gates while keeping the beta soak mandatory:
- stable still requires an explicit delivered, non-withdrawn beta whose
commit is an ancestor of the sealed release commit; the tree-identity
drift check is removed
- local releases accept any clean sealed commit contained in
origin/main history (any branch or detached HEAD) and push only the
release tag; command-compatibility checks compare the sealed HEAD,
matching CI
ossutil 2.x signs requests with V4 and refuses to run without an
explicit region, so the OSS mirror sync would fail in CI even with
valid credentials. Derive OSS_REGION from the endpoint host
(including -internal variants) and fail fast when it cannot be
derived.
* feat: add stable and beta Homebrew channels
* fix: align beta formula with tap conventions
* fix: use dedicated token for Homebrew PRs
* chore: minimize release token permissions
* docs: record Homebrew token setup
* docs: keep Homebrew automation token long-lived
* fix(verify): assert channel versions and prove homebrew coexistence
The six-channel verifier previously ran `dws version` without comparing
it to the version each channel advertises, so a stale or wrong binary
still reported PASS. It also uninstalled stable before installing beta,
which could not prove the keg-only beta coexists with stable.
- smoke() now takes an expected version and fails the channel on mismatch
- npm/homebrew derive the expected version from the package manager;
curl/upgrade derive it from the latest GitHub release tag
- homebrew installs keg-only beta while stable stays installed, then
asserts stable's version, binary SHA and PATH link are unchanged
- cleanup uninstalls both script-installed formulae, still refusing to
touch a pre-existing user install
- add regression tests for version assertion and coexistence semantics
* style(formula): satisfy brew style for stable and beta formulae
- reword desc so it no longer starts with the formula name
- drop the unnecessary `require "fileutils"` and use the mixed-in cp_r
instead of the FileUtils. qualifier
* fix(verify): compare channel versions exactly
* fix(homebrew): keep generated formulae style-clean
* fix(homebrew): sync formulae with latest releases
* 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>
The verify-replace mirror listed attachments via /releases/{id}, whose
"assets" array omits the attach id. DELETE /attach_files/{id} was therefore
called with an empty id and silently no-op'd, so a stale asset was never
removed — instead a second (correct) copy was uploaded. Gitee then serves the
OLDER attachment by name, so the stale darwin binaries kept winning and failed
install.sh's checksums.txt verification on macOS (国内 install broken).
Fix:
- List attachments via the dedicated /attach_files endpoint, which DOES
return the numeric id needed for deletion.
- Treat duplicates: collect every attach id carrying a given name; when >1,
delete them all and upload exactly one fresh, correct file. count==1 still
does the byte-identical skip / stale-replace; count==0 uploads new.
Self-heals the existing v1.0.42 darwin duplicates on the next mirror run.
The v1.0.42 Gitee release served darwin-amd64/arm64 binaries that did NOT
match checksums.txt (the macOS binaries are ad-hoc signed and differed
between the GitHub release and the earlier mirror run), so install.sh's
checksum verification failed for China macOS users. The previous skip-if-name-
present logic could not repair this — it skipped the stale assets.
sync-to-gitee.sh now verifies by content: for each artifact it compares the
sha256 of the asset already on Gitee against the local file (downloaded from
the GitHub release), and
• skips it when byte-identical,
• deletes + re-uploads it when present but stale,
• uploads it when missing,
bringing the Gitee release into byte-for-byte agreement with the GitHub
release that checksums.txt describes. Re-running the Sync-release-to-gitee
workflow now self-heals a mismatched mirror.
bash -n + sha256 helper validated locally.
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.
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.