Compare commits

...
Author SHA1 Message Date
chichuan 1b06d0105a Merge pull request #995 from DingTalk-Real-AI/codex/changelog-v1.0.58
docs: seal changelog for v1.0.58
2026-08-13 17:53:40 +08:00
chichuan 18fad57bbe docs: seal changelog for v1.0.58 2026-08-13 17:44:56 +08:00
github-actions[bot] c3ef04988b chore: update beta formula for v1.0.58-beta.6 [skip ci] 2026-08-13 09:10:23 +00:00
john 76d54d6df6 Merge pull request #993 from DingTalk-Real-AI/codex/changelog-v1.0.58-beta.6
docs: seal v1.0.58-beta.6 changelog
2026-08-13 16:33:34 +08:00
chichuan a36189d31e docs: seal v1.0.58-beta.6 changelog 2026-08-13 16:19:04 +08:00
chichuan d52d16dba4 Merge pull request #987 from DingTalk-Real-AI/codex/fix-release-seal-ci-path
ci: fast-path release seal fragment archival
2026-08-13 16:15:00 +08:00
chichuan d77fa91c69 Merge remote-tracking branch 'origin/main' into codex/fix-release-seal-ci-path 2026-08-13 15:37:08 +08:00
chichuan 9ea527a7c4 ci: reject truncated release seal file lists 2026-08-13 15:25:11 +08:00
chichuan f78f1b83e7 Merge pull request #991 from typefield/agent/fix-release-validator
fix: align package verifier with Agent skill roots
2026-08-13 15:24:10 +08:00
玉澜 3a6fa9a00c Merge remote-tracking branch 'origin/agent/fix-release-validator' into agent/fix-release-validator 2026-08-13 15:04:12 +08:00
玉澜 dc43d0d6d4 Merge remote-tracking branch 'upstream/main' into agent/fix-release-validator 2026-08-13 15:02:03 +08:00
chichuan bb69ed76df Merge branch 'main' into agent/fix-release-validator 2026-08-13 15:01:15 +08:00
chichuan a26b16b30e test: scope release seal env assertions 2026-08-13 14:58:44 +08:00
玉澜 e0c9b4910d fix: align package verifier with Agent skill roots 2026-08-13 14:57:51 +08:00
chichuan a1dc997004 Merge branch 'main' into codex/fix-release-seal-ci-path 2026-08-13 14:41:29 +08:00
chichuan b525497da8 fix: pass release seal classification to policy 2026-08-13 14:31:50 +08:00
chichuan e58b85ea45 test: cover release seal CI fast path 2026-08-13 13:44:23 +08:00
chichuan e3fef0b6d4 ci: fast-path release seal fragment archival 2026-08-13 13:34:43 +08:00
6 changed files with 296 additions and 54 deletions
+93 -12
View File
@@ -24,6 +24,7 @@ jobs:
pull-requests: read
outputs:
changelog_only: ${{ steps.classify.outputs.changelog_only }}
release_seal_only: ${{ steps.classify.outputs.release_seal_only }}
changelog_changed: ${{ steps.classify.outputs.changelog_changed }}
docs_only: ${{ steps.classify.outputs.docs_only }}
full_suite: ${{ steps.classify.outputs.full_suite }}
@@ -39,6 +40,7 @@ jobs:
with:
script: |
let changelogOnly = false;
let releaseSealOnly = false;
let changelogChanged = false;
let docsOnly = false;
let fullSuite = context.eventName === 'push';
@@ -162,6 +164,43 @@ jobs:
filename === 'go.mod' ||
filename === 'go.sum'
);
const isExactReleaseSeal = (candidates) => {
const changelog = candidates.filter(
({ filename, status, previous_filename }) =>
filename === 'CHANGELOG.md' &&
status === 'modified' &&
!previous_filename
);
if (changelog.length !== 1 || candidates.length < 2) {
return false;
}
let version = '';
return candidates.every((file) => {
if (file.filename === 'CHANGELOG.md') {
return file.status === 'modified' && !file.previous_filename;
}
if (
file.status !== 'renamed' ||
typeof file.filename !== 'string' ||
typeof file.previous_filename !== 'string' ||
file.additions !== 0 ||
file.deletions !== 0
) {
return false;
}
const target = file.filename.match(
/^\.changes\/released\/([0-9]+\.[0-9]+\.[0-9]+(?:-beta\.[1-9][0-9]*)?)\/([a-z0-9][a-z0-9._-]*\.md)$/
);
if (!target || file.previous_filename !== `.changes/${target[2]}`) {
return false;
}
if (version && version !== target[1]) {
return false;
}
version = target[1];
return true;
});
};
const classifyFiles = (complete) => {
const paths = files.flatMap(({ filename, previous_filename }) =>
[filename, previous_filename].filter(
@@ -248,19 +287,26 @@ jobs:
);
}
changelogOnly =
const exactChangelogDiff =
files.length === 1 &&
files[0].filename === 'CHANGELOG.md' &&
files[0].status === 'modified' &&
!files[0].previous_filename;
releaseSealOnly = isExactReleaseSeal(files);
changelogOnly = exactChangelogDiff || releaseSealOnly;
changelogChanged = files.some(
({ filename, previous_filename }) =>
filename === 'CHANGELOG.md' ||
previous_filename === 'CHANGELOG.md'
);
classifyFiles(true);
if (releaseSealOnly) {
fullSuite = false;
}
fastPathTrust = changelogOnly
? 'exact pull-request revision and synthetic merge policy'
? releaseSealOnly
? 'exact release-seal fragment archival and synthetic merge policy'
: 'exact CHANGELOG-only revision and synthetic merge policy'
: docsOnly
? 'documentation-only focused admission'
: fullSuite
@@ -295,7 +341,8 @@ jobs:
per_page: 100,
});
files = Array.isArray(comparison.files) ? comparison.files : [];
classifyFiles(files.length < 300);
const pushFilesComplete = files.length < 300;
classifyFiles(pushFilesComplete);
const linearFromValidatedTip =
comparison.status === 'ahead' &&
comparison.merge_base_commit?.sha === expectedBefore &&
@@ -307,8 +354,10 @@ jobs:
files[0].filename === 'CHANGELOG.md' &&
files[0].status === 'modified' &&
!files[0].previous_filename;
const exactReleaseSealDiff =
pushFilesComplete && isExactReleaseSeal(files);
if (linearFromValidatedTip && exactChangelogDiff) {
if (linearFromValidatedTip && (exactChangelogDiff || exactReleaseSealDiff)) {
const requiredContexts = [
'Lint',
'Test',
@@ -359,9 +408,15 @@ jobs:
if (missing.length === 0 && nonSuccess.length === 0) {
changelogOnly = true;
releaseSealOnly = exactReleaseSealDiff;
changelogChanged = true;
if (releaseSealOnly) {
fullSuite = false;
}
fastPathTrust =
`exact CHANGELOG-only successor of validated ${expectedBefore}`;
releaseSealOnly
? `exact release-seal successor of validated ${expectedBefore}`
: `exact CHANGELOG-only successor of validated ${expectedBefore}`;
} else {
fastPathTrust =
'predecessor Code Admission is not fully successful; ' +
@@ -376,6 +431,7 @@ jobs:
}
core.setOutput('changelog_only', String(changelogOnly));
core.setOutput('release_seal_only', String(releaseSealOnly));
core.setOutput('changelog_changed', String(changelogChanged));
core.setOutput('docs_only', String(docsOnly));
core.setOutput('full_suite', String(fullSuite));
@@ -387,7 +443,8 @@ jobs:
await core.summary
.addHeading('Code Admission scope')
.addRaw(`- Event: \`${context.eventName}\`\n`)
.addRaw(`- Exact modified CHANGELOG only: \`${changelogOnly}\`\n`)
.addRaw(`- Metadata-only fast path: \`${changelogOnly}\`\n`)
.addRaw(`- Release-seal fragments only: \`${releaseSealOnly}\`\n`)
.addRaw(`- CHANGELOG touched: \`${changelogChanged}\`\n`)
.addRaw(`- Documentation-only: \`${docsOnly}\`\n`)
.addRaw(`- Full suite: \`${fullSuite}\`\n`)
@@ -402,7 +459,14 @@ jobs:
- name: Record CHANGELOG-only fast path
if: steps.classify.outputs.changelog_only == 'true'
run: echo "Lint is satisfied by the trusted CHANGELOG-only Policy path." >> "$GITHUB_STEP_SUMMARY"
env:
RELEASE_SEAL_ONLY: ${{ steps.classify.outputs.release_seal_only }}
run: |
if [ "$RELEASE_SEAL_ONLY" = true ]; then
echo "Lint is satisfied by the trusted release-seal fragment Policy path." >> "$GITHUB_STEP_SUMMARY"
else
echo "Lint is satisfied by the trusted CHANGELOG-only Policy path." >> "$GITHUB_STEP_SUMMARY"
fi
- name: Record documentation-only fast path
if: steps.classify.outputs.changelog_only != 'true' && steps.classify.outputs.docs_only == 'true'
@@ -1256,6 +1320,7 @@ jobs:
env:
CLASSIFIED_CHANGELOG_CHANGED: ${{ needs.lint.outputs.changelog_changed }}
CHANGELOG_ONLY: ${{ needs.lint.outputs.changelog_only }}
RELEASE_SEAL_ONLY: ${{ needs.lint.outputs.release_seal_only }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
set -eu
@@ -1282,7 +1347,7 @@ jobs:
fi
mode=--content-only
if [ "$CHANGELOG_ONLY" = true ]; then
if [ "$CHANGELOG_ONLY" = true ] && [ "$RELEASE_SEAL_ONLY" != true ]; then
mode=--fast-path
fi
./scripts/policy/check-changelog-pr.sh \
@@ -1294,25 +1359,41 @@ jobs:
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: ./scripts/policy/check-release-fragments.sh "$PR_BASE_SHA" HEAD
- name: Validate trusted main CHANGELOG-only push
- name: Validate trusted main metadata-only push
if: github.event_name == 'push' && needs.lint.outputs.changelog_only == 'true'
env:
PUSH_BEFORE_SHA: ${{ github.event.before }}
PUSH_AFTER_SHA: ${{ github.event.after }}
RELEASE_SEAL_ONLY: ${{ needs.lint.outputs.release_seal_only }}
run: |
set -eu
test "$(git rev-parse HEAD)" = "$PUSH_AFTER_SHA" || {
echo "checked-out push revision does not match event after SHA" >&2
exit 1
}
mode=--fast-path
if [ "$RELEASE_SEAL_ONLY" = true ]; then
mode=--content-only
fi
./scripts/policy/check-changelog-pr.sh \
--fast-path "$PUSH_BEFORE_SHA" "$PUSH_AFTER_SHA"
"$mode" "$PUSH_BEFORE_SHA" "$PUSH_AFTER_SHA"
if [ "$RELEASE_SEAL_ONLY" = true ]; then
./scripts/policy/check-release-fragments.sh \
"$PUSH_BEFORE_SHA" "$PUSH_AFTER_SHA"
fi
- name: Record CHANGELOG-only fast path
if: needs.lint.outputs.changelog_only == 'true'
env:
RELEASE_SEAL_ONLY: ${{ needs.lint.outputs.release_seal_only }}
run: |
echo "Only the trusted base-equivalent CHANGELOG validator ran; executable sources are unchanged." \
>> "$GITHUB_STEP_SUMMARY"
if [ "$RELEASE_SEAL_ONLY" = true ]; then
echo "Only the trusted release-seal and fragment validators ran; executable sources are unchanged." \
>> "$GITHUB_STEP_SUMMARY"
else
echo "Only the trusted base-equivalent CHANGELOG validator ran; executable sources are unchanged." \
>> "$GITHUB_STEP_SUMMARY"
fi
- name: Validate scoped policy
if: ${{ needs.lint.outputs.changelog_only != 'true' && (needs.lint.outputs.docs_only == 'true' || (needs.lint.outputs.full_suite != 'true' && needs.lint.outputs.interface_sensitive != 'true')) }}
+38
View File
@@ -6,6 +6,44 @@ The format is inspired by [Keep a Changelog](https://keepachangelog.com/) and th
## [Unreleased]
## [1.0.58] - 2026-08-13
This release promotes the sealed `v1.0.58-beta.6` contents to stable.
### Changed
- **Expanded collaborative workflows** — adds full AI Table, Sheet, Minutes,
approval-event, Drive-comment, document export, and CSV workflow support,
including safer validation, explicit confirmation for writes, and
machine-readable completion receipts.
- **More capable Chat operations** — adds robot image/file messages, toolbar
management, streaming-card mentions, automatic pagination controls, and
clearer post-send ID, Markdown-image, paging, and result-shape guidance.
- **Reliable Agent and CLI contracts** — expands Agent-visible Chat and
Minutes commands, aligns bundled skills, improves schema/result envelopes,
and hardens parameter, pagination, runtime-token, and write-result
verification so ambiguous or incomplete operations fail closed.
- **Multi-skill install and upgrade** — makes the multi-skill layout the
default for fresh installs and upgrades while preserving an explicit legacy
mono option.
- **Safer release delivery** — strengthens release-equivalent compatibility,
sealing, package verification, and evaluation-dispatch checks for more
reliable cross-platform releases.
## [1.0.58-beta.6] - 2026-08-13
### Fixed
- **npm package verification for multi-skill installs** (#991) — aligns the
release verifier with the installer’s concrete Agent skill-root selection,
preventing valid multi-skill package layouts from failing release delivery.
### Changed
- **Release-seal CI classification** (#987) — recognizes the reviewed
CHANGELOG-and-fragment archival shape while retaining release-contract and
lifecycle validation, reducing unrelated CI work for release-seal PRs.
## [1.0.58-beta.5] - 2026-08-13
### Added
+11 -11
View File
@@ -1,33 +1,33 @@
class DingtalkWorkspaceCliBeta < Formula
desc "Automate DingTalk workspace tasks from the terminal (beta channel)"
homepage "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli"
version "1.0.58-beta.4"
version "1.0.58-beta.6"
license "Apache-2.0"
keg_only "it is the beta channel and conflicts with dingtalk-workspace-cli"
on_macos do
if Hardware::CPU.arm?
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.4/dws-darwin-arm64.tar.gz"
sha256 "5c2ac92e35b1f1dba80234af8b0c9505b2883f4a37c1e73892b8a1c3087b7702"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.6/dws-darwin-arm64.tar.gz"
sha256 "8f55497b84113f81b318e087c723016a02eded1cb5784cbd0811fe527d5852ca"
else
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.4/dws-darwin-amd64.tar.gz"
sha256 "93ef787770105fe1f0d27585adcac7b740aa6c37ff490275c4113814541ae095"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.6/dws-darwin-amd64.tar.gz"
sha256 "b1762f1640310fb4100634fe54d9baace46384bb270c59148fe306275554d491"
end
end
on_linux do
if Hardware::CPU.arm?
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.4/dws-linux-arm64.tar.gz"
sha256 "011ce16a73d8fd24275e34c3122d3d0832c60cde2480f496018eb654059b5c05"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.6/dws-linux-arm64.tar.gz"
sha256 "55393310ef0e1f24ea2c0dc22f00c0eb3b343edc2deb18d0db7838cda65af25d"
else
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.4/dws-linux-amd64.tar.gz"
sha256 "847b17ff8a8d80dce38f0013eb35c77c102be16c9f98b955a632b983cd5ec104"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.6/dws-linux-amd64.tar.gz"
sha256 "3830f77d09b4da4aa39f0c08d772ff3fa1ffb837eb15bb417f97edbccb073b7d"
end
end
resource "skills" do
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.4/dws-skills.zip"
sha256 "f5e0c72cc92cb7e8886409319cf68bbbfc7740e969bd39a389b74af4befdbc66"
url "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/download/v1.0.58-beta.6/dws-skills.zip"
sha256 "f304a883a4f9e938b26a44692cd5a8d3d8704ba70ee7f33ba7c288434da72b6f"
end
def install
+57 -28
View File
@@ -54,7 +54,7 @@ while [ "$#" -gt 0 ]; do
done
TMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/dws-package-verify-XXXXXX")"
HOME_AGENT_PARENTS="
HOME_SPECIFIC_AGENT_PARENTS="
.claude
.cursor
.qoder
@@ -72,8 +72,7 @@ HOME_AGENT_PARENTS="
.openclaw
.hermes
"
HOME_SKILL_BASES="
.agents/skills
HOME_SPECIFIC_SKILL_BASES="
.claude/skills
.cursor/skills
.qoder/skills
@@ -102,43 +101,55 @@ cleanup() {
}
trap cleanup EXIT INT TERM
seed_agent_homes() {
seed_specific_agent_homes() {
home_root="$1"
for parent in $HOME_AGENT_PARENTS; do
for parent in $HOME_SPECIFIC_AGENT_PARENTS; do
mkdir -p "$home_root/$parent"
done
}
verify_skill_base() {
home_root="$1"
base="$2"
need_file "$home_root/$base/dingtalk-shared/SKILL.md"
need_file "$home_root/$base/dingtalk-misc/SKILL.md"
[ ! -e "$home_root/$base/dws" ] || err "unexpected mono Skill layout found in $home_root/$base/dws"
}
verify_skill_targets() {
home_root="$1"
for base in $HOME_SKILL_BASES; do
need_file "$home_root/$base/dingtalk-shared/SKILL.md"
need_file "$home_root/$base/dingtalk-misc/SKILL.md"
[ ! -e "$home_root/$base/dws" ] || err "unexpected mono Skill layout found in $home_root/$base/dws"
found_specific=0
for base in $HOME_SPECIFIC_SKILL_BASES; do
parent="${base%/skills}"
if [ -d "$home_root/$parent" ]; then
verify_skill_base "$home_root" "$base"
found_specific=1
fi
done
if [ "$found_specific" -eq 0 ]; then
verify_skill_base "$home_root" ".agents/skills"
return
fi
for name in dingtalk-shared dingtalk-misc dws; do
[ ! -e "$home_root/.agents/skills/$name" ] || \
err "unexpected generic Skill copy found in $home_root/.agents/skills/$name"
done
}
verify_npm() {
need_cmd npm
need_cmd node
need_cmd tar
need_cmd unzip
need_file "$NPM_STAGE_DIR/package.json"
npm_home="$TMP_ROOT/npm-home"
npm_prefix="$TMP_ROOT/npm-prefix"
npm_cache="$TMP_ROOT/npm-cache"
verify_npm_install() {
tarball_path="$1"
scenario="$2"
npm_home="$TMP_ROOT/npm-home-$scenario"
npm_prefix="$TMP_ROOT/npm-prefix-$scenario"
npm_cache="$TMP_ROOT/npm-cache-$scenario"
mkdir -p "$npm_home" "$npm_prefix" "$npm_cache"
seed_agent_homes "$npm_home"
say "==> verifying npm package install"
tarball_name="$(
cd "$NPM_STAGE_DIR"
HOME="$npm_home" npm_config_cache="$npm_cache" npm pack --silent
)"
tarball_path="$NPM_STAGE_DIR/$tarball_name"
need_file "$tarball_path"
if [ "$scenario" = "specific-agent-roots" ]; then
seed_specific_agent_homes "$npm_home"
fi
say "==> verifying npm package install ($scenario)"
HOME="$npm_home" npm_config_cache="$npm_cache" npm_config_prefix="$npm_prefix" \
npm install -g "$tarball_path" >/dev/null
@@ -158,6 +169,24 @@ verify_npm() {
HOME="$npm_home" npm_config_cache="$npm_cache" npm_config_prefix="$npm_prefix" \
npm uninstall -g dingtalk-workspace-cli >/dev/null
}
verify_npm() {
need_cmd npm
need_cmd node
need_cmd tar
need_cmd unzip
need_file "$NPM_STAGE_DIR/package.json"
tarball_name="$(
cd "$NPM_STAGE_DIR"
HOME="$TMP_ROOT/npm-pack-home" npm_config_cache="$TMP_ROOT/npm-pack-cache" npm pack --silent
)"
tarball_path="$NPM_STAGE_DIR/$tarball_name"
need_file "$tarball_path"
verify_npm_install "$tarball_path" "specific-agent-roots"
verify_npm_install "$tarball_path" "generic-fallback"
rm -f "$tarball_path"
}
+40 -2
View File
@@ -1019,12 +1019,47 @@ func TestChangelogPRFastPathWorkflowContract(t *testing.T) {
}
admission := readWorkflow(".github/workflows/ci.yml")
policyStart := strings.Index(admission, "\n policy:\n")
policyEnd := strings.Index(admission, "\n interface-integrity:\n")
if policyStart < 0 || policyEnd <= policyStart {
t.Fatal("Code Admission workflow missing Policy job boundaries")
}
policyJob := admission[policyStart:policyEnd]
requirePolicyEnv := func(step, nextStep string) {
t.Helper()
start := strings.Index(policyJob, " - name: "+step+"\n")
end := strings.Index(policyJob[start+1:], " - name: "+nextStep+"\n")
if start < 0 || end < 0 {
t.Fatalf("Policy job missing %q step boundary", step)
}
block := policyJob[start : start+1+end]
if !strings.Contains(block, "RELEASE_SEAL_ONLY: ${{ needs.lint.outputs.release_seal_only }}") {
t.Errorf("Policy %q step must receive release-seal classification", step)
}
}
requirePolicyEnv("Validate changed CHANGELOG content", "Validate release fragment lifecycle")
requirePolicyEnv("Validate trusted main metadata-only push", "Record CHANGELOG-only fast path")
pushStart := strings.Index(admission, "} else if (context.eventName === 'push') {")
pushEnd := strings.Index(admission, "\n core.setOutput('changelog_only'")
if pushStart < 0 || pushEnd <= pushStart {
t.Fatal("Code Admission workflow missing push classification boundaries")
}
pushClassification := admission[pushStart:pushEnd]
if strings.Contains(pushClassification, "const exactReleaseSealDiff = isExactReleaseSeal(files)") {
t.Error("push release-seal fast path must not trust an unguarded compare file list")
}
for _, want := range []string{
"name: CI",
"files.length === 1",
"files[0].filename === 'CHANGELOG.md'",
"files[0].status === 'modified'",
"!files[0].previous_filename",
"const isExactReleaseSeal",
"file.status !== 'renamed'",
"file.additions !== 0",
"file.deletions !== 0",
"releaseSealOnly = isExactReleaseSeal(files)",
"release_seal_only: ${{ steps.classify.outputs.release_seal_only }}",
"pre-classification",
"post-classification",
"before.changed_files !== files.length",
@@ -1048,7 +1083,8 @@ func TestChangelogPRFastPathWorkflowContract(t *testing.T) {
"mode=--fast-path",
`"$mode" "$PR_BASE_SHA" HEAD`,
`test "$(git rev-parse HEAD)" = "$PUSH_AFTER_SHA"`,
`--fast-path "$PUSH_BEFORE_SHA" "$PUSH_AFTER_SHA"`,
`"$mode" "$PUSH_BEFORE_SHA" "$PUSH_AFTER_SHA"`,
`./scripts/policy/check-release-fragments.sh \`,
"needs.lint.outputs.platform_sensitive == 'true'",
`COVERAGE_TARGET: "100"`,
`COVERAGE_ENFORCE_OVERALL: "false"`,
@@ -1060,7 +1096,9 @@ func TestChangelogPRFastPathWorkflowContract(t *testing.T) {
`package_output="$(./scripts/ci/test-packages.sh list`,
"docs_only: ${{ steps.classify.outputs.docs_only }}",
"full_suite: ${{ steps.classify.outputs.full_suite }}",
"classifyFiles(files.length < 300)",
"const pushFilesComplete = files.length < 300",
"classifyFiles(pushFilesComplete)",
"pushFilesComplete && isExactReleaseSeal(files)",
"platformSensitive =",
"files.some(isNativeGoChange)",
"paths.some(isEditionSensitive)",
+57 -1
View File
@@ -70,10 +70,12 @@ func TestPackageManagerVersionVerificationReadsRawBinary(t *testing.T) {
t.Fatal("package-manager verifier still requires the version marker to occupy a strings(1) line")
}
for _, want := range []string{
"HOME_SKILL_BASES=",
"HOME_SPECIFIC_SKILL_BASES=",
`$base/dingtalk-shared/SKILL.md`,
`$base/dingtalk-misc/SKILL.md`,
"unexpected mono Skill layout",
`verify_npm_install "$tarball_path" "specific-agent-roots"`,
`verify_npm_install "$tarball_path" "generic-fallback"`,
} {
if !strings.Contains(script, want) {
t.Errorf("package-manager verifier is missing multi-layout contract %q", want)
@@ -84,6 +86,60 @@ func TestPackageManagerVersionVerificationReadsRawBinary(t *testing.T) {
}
}
func TestPackageManagerVerifierCoversSpecificAndFallbackSkillRoots(t *testing.T) {
t.Parallel()
postGoreleaserPath, err := filepath.Abs(filepath.Join("..", "..", "scripts", "release", "post-goreleaser.sh"))
if err != nil {
t.Fatalf("Abs(post-goreleaser.sh) error = %v", err)
}
verifierPath, err := filepath.Abs(filepath.Join("..", "..", "scripts", "release", "verify-package-managers.sh"))
if err != nil {
t.Fatalf("Abs(verify-package-managers.sh) error = %v", err)
}
distDir := filepath.Join(t.TempDir(), "dist")
targets := []string{
"dws-darwin-amd64.tar.gz",
"dws-darwin-arm64.tar.gz",
"dws-linux-amd64.tar.gz",
"dws-linux-arm64.tar.gz",
}
hostArchive := "dws-" + runtime.GOOS + "-" + runtime.GOARCH + ".tar.gz"
if runtime.GOOS == "windows" {
hostArchive = "dws-" + runtime.GOOS + "-" + runtime.GOARCH + ".zip"
}
foundHost := false
for _, target := range targets {
if target == hostArchive {
foundHost = true
break
}
}
if !foundHost {
targets = append(targets, hostArchive)
}
seedDistArtifacts(t, distDir, targets)
packageCmd := exec.Command("sh", postGoreleaserPath)
packageCmd.Env = postGoreleaserEnv(t, distDir, "v0.0.0-test", "https://downloads.example.com/dws/releases/v0.0.0-test")
if output, err := packageCmd.CombinedOutput(); err != nil {
t.Fatalf("post-goreleaser.sh error = %v\noutput:\n%s", err, output)
}
verifyCmd := exec.Command("sh", verifierPath, "--npm-only")
verifyCmd.Env = append(os.Environ(), "DWS_PACKAGE_DIST_DIR="+distDir)
output, err := verifyCmd.CombinedOutput()
if err != nil {
t.Fatalf("verify-package-managers.sh error = %v\noutput:\n%s", err, output)
}
for _, scenario := range []string{"specific-agent-roots", "generic-fallback"} {
if !strings.Contains(string(output), "verifying npm package install ("+scenario+")") {
t.Errorf("verifier output is missing %s scenario:\n%s", scenario, output)
}
}
}
func seedDistArchive(t *testing.T, path string) {
t.Helper()
file, err := os.Create(path)