mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
d18ada9e35
The maintainer who received this alert could not tell what it was. The headline jumped straight to a verdict, so nothing said an upstream release had been detected or where the message came from; the body opened with semver theory; the blocking branch never mentioned the version we pin, making the most urgent card the least informative one; and nothing said when upstream shipped, so there was no way to tell a release from this morning apart from one that had been sitting for a fortnight. The headline now names the check and only the check — severity travels in the header template, which Feishu renders before any text. The first sentence is three facts with no reasoning attached: what upstream released, when, and what we still pin, on both severities. The consequence follows, and the semver rationale is compressed into a parenthetical behind it. The three-file checklist is untouched. Reading the publish date means asking npm for the full packument: the compact one (`application/vnd.npm.install-v1+json`) carries no `time` map at all. `readPublishedAt` degrades to null for every shape short of that, because the date is one sentence of context on an alert whose entire reason to exist is being sent. Also replaces a self-check assertion that could never fail: `String(card.header).length === 0` is "[object Object]".length whatever the card says. It now reads the fields a human reads. Claude-Session: https://claude.ai/code/session_01B8MaA1ABkvbnfaYUKw3UH3
320 lines
12 KiB
TypeScript
320 lines
12 KiB
TypeScript
// The upstream watch exists because the in-repo drift check cannot see the one
|
||
// thing that actually went wrong twice: our installers and our agent def
|
||
// agreeing with each other while both sat behind what npm serves. These cases
|
||
// pin that distinction, plus the parsing that lets the checker read either side
|
||
// out of source rather than being told the versions twice.
|
||
|
||
import { readFile } from 'node:fs/promises';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
import { describe, expect, it, vi } from 'vitest';
|
||
|
||
import {
|
||
type DriftCard,
|
||
buildCard,
|
||
classifyDrift,
|
||
describePublishedAt,
|
||
interpretFeishuResponse,
|
||
readAcceptedPattern,
|
||
readListedVersions,
|
||
readPinnedVersion,
|
||
readPublishedAt,
|
||
} from '../../../.github/scripts/dsh-upstream-drift.ts';
|
||
|
||
const repoRoot = fileURLToPath(new URL('../../../', import.meta.url));
|
||
const INSTALLER_SH = `${repoRoot}tools/release/resources/dsh-bootstrap/install-dsh.sh`;
|
||
const AGENT_DEF = `${repoRoot}apps/daemon/src/runtimes/defs/deepseek-harness.ts`;
|
||
const DRIFT_SCRIPT = `${repoRoot}.github/scripts/dsh-upstream-drift.ts`;
|
||
const WORKFLOW = `${repoRoot}.github/workflows/dsh-upstream-drift.yml`;
|
||
|
||
/** A fixed "now" so the card's relative age is a fact, not a moving target. */
|
||
const NOW = new Date('2026-09-07T00:00:00.000Z');
|
||
const PUBLISHED_AT = '2026-09-03T06:21:52.107Z';
|
||
|
||
function headlineOf(card: DriftCard): string {
|
||
return card.header.title.content;
|
||
}
|
||
|
||
/** Everything the reader actually reads under the headline. */
|
||
function bodyOf(card: DriftCard): string {
|
||
return card.elements.map((element) => element.text?.content ?? '').join('\n');
|
||
}
|
||
|
||
describe('DeepSeek Harness upstream drift', () => {
|
||
it('reads what we currently ship out of the real files', async () => {
|
||
const [installer, def, script] = await Promise.all([
|
||
readFile(INSTALLER_SH, 'utf8'),
|
||
readFile(AGENT_DEF, 'utf8'),
|
||
readFile(DRIFT_SCRIPT, 'utf8'),
|
||
]);
|
||
|
||
// Canonical product path — not the temporary landing public copies.
|
||
expect(script).toContain(
|
||
'tools/release/resources/dsh-bootstrap/install-dsh.sh',
|
||
);
|
||
expect(script).not.toContain('apps/landing-page/public/install-dsh.sh');
|
||
|
||
const pinned = readPinnedVersion(installer);
|
||
expect(pinned).toMatch(/^\d+\.\d+\.\d+/u);
|
||
|
||
// Whatever the installer hands a user has to be a version the daemon
|
||
// accepts, or we ship an "untested" warning to anyone who follows our own
|
||
// instructions. The dedicated guard for that pairing is
|
||
// `e2e/tests/dsh-installer-version-policy.test.ts`; this asserts the
|
||
// checker can see both sides, which is what makes its verdict meaningful.
|
||
const pattern = readAcceptedPattern(def);
|
||
const listed = readListedVersions(def);
|
||
expect(listed.length).toBeGreaterThan(0);
|
||
expect(listed.includes(pinned) || (pattern?.test(pinned) ?? false)).toBe(true);
|
||
});
|
||
|
||
// The state that shipped twice. Both halves of our config agree, so the
|
||
// in-repo check is green, and a user on what npm serves is outside what we
|
||
// support — Settings calls their CLI untested and the peer ranges will not
|
||
// resolve. Anything that reports this as healthy has lost the point.
|
||
it('calls an accepted-nowhere latest unsupported, not merely behind', () => {
|
||
const verdict = classifyDrift({
|
||
accepted: false,
|
||
latest: '0.1.1-rc.2',
|
||
pinned: '0.1.0-rc.8',
|
||
publishedAt: PUBLISHED_AT,
|
||
});
|
||
|
||
expect(verdict.severity).toBe('unsupported');
|
||
expect(JSON.stringify(buildCard(verdict, NOW))).toContain('0.1.1-rc.2');
|
||
});
|
||
|
||
it('separates "we ship an older version" from "we do not support theirs"', () => {
|
||
expect(
|
||
classifyDrift({
|
||
accepted: true,
|
||
latest: '0.1.2',
|
||
pinned: '0.1.1-rc.2',
|
||
publishedAt: PUBLISHED_AT,
|
||
}).severity,
|
||
).toBe('behind');
|
||
expect(
|
||
classifyDrift({
|
||
accepted: true,
|
||
latest: '0.1.1-rc.2',
|
||
pinned: '0.1.1-rc.2',
|
||
publishedAt: PUBLISHED_AT,
|
||
}).severity,
|
||
).toBe('in-sync');
|
||
});
|
||
|
||
// The maintainer who received this card could not tell what it was: the
|
||
// headline jumped straight to a verdict, so nothing said "an upstream release
|
||
// was detected" or where the message came from. The check's name belongs in
|
||
// the headline; how bad it is belongs in the header colour, which is the one
|
||
// thing a Feishu card renders before any text.
|
||
it('names the check in the headline instead of announcing a verdict there', () => {
|
||
const blocking = buildCard(
|
||
classifyDrift({
|
||
accepted: false,
|
||
latest: '0.1.2-rc.1',
|
||
pinned: '0.1.1-rc.2',
|
||
publishedAt: PUBLISHED_AT,
|
||
}),
|
||
NOW,
|
||
);
|
||
const behind = buildCard(
|
||
classifyDrift({
|
||
accepted: true,
|
||
latest: '0.1.2',
|
||
pinned: '0.1.1-rc.2',
|
||
publishedAt: PUBLISHED_AT,
|
||
}),
|
||
NOW,
|
||
);
|
||
|
||
expect(headlineOf(blocking)).toContain('DeepSeek Harness');
|
||
expect(headlineOf(blocking)).toContain('新版检测');
|
||
// Same check, same name. Severity travels in the template, not the title.
|
||
expect(headlineOf(behind)).toBe(headlineOf(blocking));
|
||
expect(blocking.header.template).toBe('red');
|
||
expect(behind.header.template).toBe('orange');
|
||
});
|
||
|
||
// The blocking branch used to be the least informative one: its body never
|
||
// mentioned `pinned` at all, so the most urgent card was the one that hid
|
||
// what we currently ship. Both severities owe the reader the same three
|
||
// facts before any explanation: what upstream released, when, and where we
|
||
// still sit.
|
||
it.each([
|
||
{ accepted: false, latest: '0.1.2-rc.1', severity: 'unsupported' },
|
||
{ accepted: true, latest: '0.1.2', severity: 'behind' },
|
||
])('states upstream version, publish date and our pin ($severity)', (scenario) => {
|
||
const verdict = classifyDrift({
|
||
accepted: scenario.accepted,
|
||
latest: scenario.latest,
|
||
pinned: '0.1.1-rc.2',
|
||
publishedAt: PUBLISHED_AT,
|
||
});
|
||
expect(verdict.severity).toBe(scenario.severity);
|
||
|
||
const body = bodyOf(buildCard(verdict, NOW));
|
||
|
||
expect(body).toContain(scenario.latest);
|
||
expect(body).toContain('0.1.1-rc.2');
|
||
expect(body).toContain('2026-09-03');
|
||
// "Is this fresh, or has it been sitting for two weeks?" is the question a
|
||
// bare timestamp still leaves open.
|
||
expect(body).toContain('4 天前');
|
||
|
||
// What happened comes before why it matters: the semver rationale used to
|
||
// be the opening sentence of the card.
|
||
const firstBlock = buildCard(verdict, NOW).elements[0]?.text?.content ?? '';
|
||
expect(firstBlock).toContain(scenario.latest);
|
||
expect(firstBlock).toContain('0.1.1-rc.2');
|
||
expect(firstBlock).not.toContain('semver');
|
||
|
||
// The three-file checklist is the useful half of the old card; keep it.
|
||
expect(body).toContain('packages/dsh-runtime/package.json');
|
||
});
|
||
|
||
// The publish date is decoration on an alert whose only job is to be sent.
|
||
// A registry response without it must cost the card one sentence, not the
|
||
// whole alert.
|
||
it('drops the publish date rather than the alert when the registry omits it', () => {
|
||
const card = buildCard(
|
||
classifyDrift({
|
||
accepted: false,
|
||
latest: '0.1.2-rc.1',
|
||
pinned: '0.1.1-rc.2',
|
||
publishedAt: null,
|
||
}),
|
||
NOW,
|
||
);
|
||
const body = bodyOf(card);
|
||
|
||
expect(headlineOf(card)).toContain('新版检测');
|
||
expect(body).toContain('0.1.2-rc.1');
|
||
expect(body).toContain('0.1.1-rc.2');
|
||
expect(body).not.toContain('null');
|
||
expect(body).not.toContain('Invalid Date');
|
||
expect(body).not.toContain('NaN');
|
||
});
|
||
|
||
// The compact packument (`application/vnd.npm.install-v1+json`) carries only
|
||
// name / dist-tags / versions / modified — no `time` map at all — so reading
|
||
// the publish date has to survive every shape short of the full document.
|
||
it('reads the publish date out of a packument, and null out of anything else', () => {
|
||
const full = {
|
||
'dist-tags': { latest: '0.1.2-rc.1' },
|
||
name: '@deepseek-ai/dsh',
|
||
time: {
|
||
'0.1.1-rc.2': '2026-08-21T09:02:11.000Z',
|
||
'0.1.2-rc.1': PUBLISHED_AT,
|
||
created: '2026-01-04T00:00:00.000Z',
|
||
modified: PUBLISHED_AT,
|
||
},
|
||
};
|
||
|
||
expect(readPublishedAt(full, '0.1.2-rc.1')).toBe(PUBLISHED_AT);
|
||
expect(readPublishedAt(full, '9.9.9')).toBeNull();
|
||
// The compact packument shape.
|
||
expect(
|
||
readPublishedAt(
|
||
{ 'dist-tags': { latest: '0.1.2-rc.1' }, modified: PUBLISHED_AT, name: 'x' },
|
||
'0.1.2-rc.1',
|
||
),
|
||
).toBeNull();
|
||
expect(readPublishedAt({ time: { '0.1.2-rc.1': 17 } }, '0.1.2-rc.1')).toBeNull();
|
||
expect(readPublishedAt(null, '0.1.2-rc.1')).toBeNull();
|
||
expect(readPublishedAt('not json', '0.1.2-rc.1')).toBeNull();
|
||
});
|
||
|
||
it('turns a publish timestamp into a date plus an age, and garbage into nothing', () => {
|
||
expect(describePublishedAt(PUBLISHED_AT, NOW)).toContain('2026-09-03');
|
||
expect(describePublishedAt(PUBLISHED_AT, NOW)).toContain('4 天前');
|
||
expect(describePublishedAt('2026-09-07T01:00:00.000Z', new Date('2026-09-07T05:00:00Z'))).toBe(
|
||
'2026-09-07(今天)',
|
||
);
|
||
expect(describePublishedAt(null, NOW)).toBeNull();
|
||
expect(describePublishedAt('yesterday-ish', NOW)).toBeNull();
|
||
});
|
||
|
||
it('rebuilds the accepted pattern from source rather than restating it', () => {
|
||
const pattern = readAcceptedPattern(
|
||
" supportedVersionPattern: /^0\\.1\\.\\d+(?:-rc\\.\\d+)?$/u,",
|
||
);
|
||
|
||
expect(pattern?.test('0.1.1-rc.2')).toBe(true);
|
||
expect(pattern?.test('0.1.9')).toBe(true);
|
||
expect(pattern?.test('0.2.0-rc.1')).toBe(false);
|
||
});
|
||
|
||
it('treats a def with no pattern as accepting only what it lists', () => {
|
||
expect(readAcceptedPattern(" supportedVersions: ['0.1.0-rc.8'],")).toBeNull();
|
||
});
|
||
|
||
|
||
// Importing the module used to run the CLI: collecting this very file fetched
|
||
// the live registry and, once drift existed, would have tried to post to
|
||
// Feishu before a single assertion ran. A watcher that fires from a test run
|
||
// is worse than no watcher.
|
||
it('does not touch the network when imported', async () => {
|
||
const realFetch = globalThis.fetch;
|
||
const calls: string[] = [];
|
||
globalThis.fetch = (async (input: unknown) => {
|
||
calls.push(String(input));
|
||
throw new Error('the drift script must not fetch on import');
|
||
}) as typeof globalThis.fetch;
|
||
|
||
try {
|
||
vi.resetModules();
|
||
await import('../../../.github/scripts/dsh-upstream-drift.ts');
|
||
expect(calls).toEqual([]);
|
||
} finally {
|
||
globalThis.fetch = realFetch;
|
||
}
|
||
});
|
||
|
||
// Feishu answers a rejected webhook with HTTP 200 and a nonzero code, so
|
||
// "2xx means delivered" throws the alert away and reports success doing it.
|
||
it('only counts a Feishu response as delivered when the app-level code says so', () => {
|
||
expect(interpretFeishuResponse({ status: 200, text: '{"code":0}' })).toMatchObject({
|
||
delivered: true,
|
||
});
|
||
expect(interpretFeishuResponse({ status: 200, text: '{"StatusCode":0}' })).toMatchObject({
|
||
delivered: true,
|
||
});
|
||
expect(
|
||
interpretFeishuResponse({ status: 200, text: '{"code":19021,"msg":"sign match fail"}' }),
|
||
).toMatchObject({ code: 19021, delivered: false, retryable: false });
|
||
});
|
||
|
||
it('retries only what is worth retrying', () => {
|
||
expect(interpretFeishuResponse({ status: 429, text: '' }).retryable).toBe(true);
|
||
expect(interpretFeishuResponse({ status: 503, text: '' }).retryable).toBe(true);
|
||
expect(interpretFeishuResponse({ status: 200, text: '{"code":9499}' }).retryable).toBe(true);
|
||
expect(interpretFeishuResponse({ status: 400, text: '{"code":19001}' }).retryable).toBe(false);
|
||
});
|
||
|
||
// A watch that only runs on a green PR would never fire, since an upstream
|
||
// release does not touch this repo.
|
||
it('runs on a schedule and stays outside the merge gate', async () => {
|
||
const workflow = await readFile(WORKFLOW, 'utf8');
|
||
|
||
expect(workflow).toMatch(/^on:\n\s+schedule:/mu);
|
||
expect(workflow).toContain('workflow_dispatch');
|
||
expect(workflow).toContain('dsh-upstream-drift.ts self-check');
|
||
expect(workflow).not.toContain('pull_request');
|
||
});
|
||
|
||
// The webhook and its signing secret are a pair. Selecting the landing bot
|
||
// while signing with the release bot's secret sends a card Feishu rejects,
|
||
// which is a silent loss of the only message this workflow exists to send.
|
||
it('signs with the secret belonging to the webhook it chose', async () => {
|
||
const workflow = await readFile(WORKFLOW, 'utf8');
|
||
|
||
expect(workflow).toContain(
|
||
'secrets.FEISHU_LANDING_WEBHOOK || secrets.FEISHU_RELEASE_WEBHOOK',
|
||
);
|
||
expect(workflow).toContain(
|
||
'secrets.FEISHU_LANDING_SIGN_SECRET || secrets.FEISHU_RELEASE_SIGN_SECRET',
|
||
);
|
||
});
|
||
});
|