From 5a6c34b57805a788a1b20951f52b99ce3f23956a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 22:08:48 +0000 Subject: [PATCH 1/4] =?UTF-8?q?fix(react-runtime,sdui-parser):=20=E8=A1=A5?= =?UTF-8?q?=E4=B8=8A=E4=B8=A4=E5=8C=85=E5=A3=B0=E6=98=8E=E4=BA=86=20MIT=20?= =?UTF-8?q?=E5=8D=B4=E4=BB=8E=E6=9C=AA=E9=9A=8F=E5=8C=85=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E7=9A=84=E8=AE=B8=E5=8F=AF=E8=AF=81=E6=96=87=E6=9C=AC=20(#3696?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两个包的 package.json 都写着 "license": "MIT",但仓库里没有对应的许可证文本, 所以每一个已发布的 tarball 都不含 MIT 要求随分发附带的许可证与版权声明。 这不是 files 声明问题:npm 有一份无视 files 恒定打包的清单(package.json、 README、LICENSE/LICENCE、COPYING、main 指向的文件),files: ["dist"] 并不会 把 LICENSE 排除在外。缺失的原因是磁盘上根本没有该文件,所以修法是补文件, 不是改 files —— 本 PR 未动任何 package.json。 两份 LICENSE 均为仓根 LICENSE 的逐字节副本,blob 与另外 37 份同为 cf2ca28574caca42deafdee0ea935fc2e2823427。 同时给 package-files-exist 门禁加一道独立断言块:凡非 private 且声明了 license 字段的包,必须有许可证文本。判据从工作区扫描派生,不是硬编码包名单, 所以第 40 个包加进来的当天就被覆盖 —— #3647 与 #3696 连着两次都是靠人肉逐包 普查才发现的。 该门禁同时点名了 apps/console(@object-ui/console,publishConfig.access: public,同一缺陷),它超出本单文件面,已挂 #3702 并写进双向 ratchet 基线。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt --- packages/react-runtime/LICENSE | 21 ++ packages/sdui-parser/LICENSE | 21 ++ scripts/__tests__/package-files-exist.test.ts | 211 ++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 packages/react-runtime/LICENSE create mode 100644 packages/sdui-parser/LICENSE diff --git a/packages/react-runtime/LICENSE b/packages/react-runtime/LICENSE new file mode 100644 index 0000000000..cf2ca28574 --- /dev/null +++ b/packages/react-runtime/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 ObjectQL + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/sdui-parser/LICENSE b/packages/sdui-parser/LICENSE new file mode 100644 index 0000000000..cf2ca28574 --- /dev/null +++ b/packages/sdui-parser/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 ObjectQL + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/scripts/__tests__/package-files-exist.test.ts b/scripts/__tests__/package-files-exist.test.ts index 3278e2468e..1a0f4720c4 100644 --- a/scripts/__tests__/package-files-exist.test.ts +++ b/scripts/__tests__/package-files-exist.test.ts @@ -85,6 +85,8 @@ interface WorkspacePackage { private: boolean; files: string[] | undefined; hasBuildScript: boolean; + /** The `license` field verbatim, or undefined when the manifest omits it. */ + license: string | undefined; } /** @@ -147,6 +149,7 @@ function readWorkspacePackages(): WorkspacePackage[] { private: Boolean(json.private), files: Array.isArray(json.files) ? json.files : undefined, hasBuildScript: Boolean(json.scripts?.build), + license: typeof json.license === 'string' ? json.license : undefined, }); } } @@ -348,3 +351,211 @@ describe('package.json `files` entries exist on disk (objectui#3663)', () => { ).toBe(true); }); }); + +/** + * objectui#3696: the guard above can only ask whether a path a package + * PROMISED in `files` is real. A package that never made the promise is + * invisible to it — and the license text is exactly the file nobody promises, + * because npm ships it whether you list it or not. + * + * ## Why `files` is the wrong place to look for this one + * + * npm keeps a small set of paths it packs REGARDLESS of `files`: + * `package.json`, `README*`, `LICENSE*`/`LICENCE*`, `COPYING*`, and the `main` + * entry. Measured on `packages/sdui-parser` (which declares `files: ["dist"]`) + * by renaming one file and re-running `npm pack --dry-run`: + * + * LICENSE / License / LICENCE / LICENSE.md / LICENSE.txt -> packed + * copying -> packed + * NOTICE / CHANGELOG.md / NOTALICENSE -> NOT packed + * + * So `files: ["dist"]` never excluded the license text, and adding `"LICENSE"` + * to `files` would not have included it. `@object-ui/react-runtime` and + * `@object-ui/sdui-parser` shipped every version of themselves declaring + * `"license": "MIT"` with no MIT text and no copyright notice in the tarball, + * for one reason only: the file was not on disk. That is what this guard reads. + * + * ## The predicate + * + * `!private && license` — the two conditions that together turn "MIT" from a + * label into an obligation. A private package makes no distribution and owes no + * text (`object-ui`, the vscode-extension, is exactly that case and is excluded + * on purpose). A package declaring no license at all is a different defect that + * this guard deliberately does not invent a verdict about. + * + * The population is DERIVED from the same workspace scan as the guard above, so + * a 40th package is covered the day it is added rather than the day someone + * repeats the by-hand census. objectui#3647 and objectui#3696 were both found + * by a human diffing every package by hand; twice in one week is the argument + * for a gate, not for more care. + */ + +/** + * Spellings that count as license text. The `licen[cs]e` family from npm's + * always-packed set above, with an optional extension. + * + * `COPYING` is packed by npm too but is deliberately NOT accepted: every one of + * this repo's 38 license files is spelled `LICENSE`, and a guard that silently + * blesses a spelling the repo does not use would report success over a + * convention drift. Landing a `COPYING` should turn this red and be widened + * here on purpose. + */ +const LICENSE_TEXT_RE = /^licen[cs]e(\.[^.]+)?$/i; + +/** Every license-text file in a package directory, by name. */ +function licenseTextFiles(pkg: WorkspacePackage): string[] { + const dir = path.join(repoRoot, pkg.dir); + if (!fs.existsSync(dir)) return []; + return fs + .readdirSync(dir) + .filter((name) => LICENSE_TEXT_RE.test(name)) + .sort(); +} + +/** + * Whether a package owes license text. Split out from the filter below so the + * truth table can be asserted directly: two of its four cases have no specimen + * in the tree today, and an unexercised predicate limb is how objectui#4984's + * family of dead rules started. + */ +function owesLicenseText(pkg: Pick): boolean { + return !pkg.private && pkg.license !== undefined && pkg.license.trim() !== ''; +} + +const owingPackages = packages.filter(owesLicenseText); +const withoutLicenseText = owingPackages.filter((p) => licenseTextFiles(p).length === 0); + +/** + * Packages that owed license text and did not have it when this guard landed, + * measured on main@3e601773e. + * + * A RATCHET in the same shape as {@link KNOWN_MISSING}: a NEW offender fails + * without consulting this map, and an entry here that is no longer offending + * fails too, so it can only shrink. + * + * `apps/console` (`@object-ui/console`) is the one entry. It is published for + * real — `publishConfig.access: "public"`, versioned 17.3.0 with the rest, in + * the `.changeset/config.json` `fixed` group — and it has the identical defect + * to the two packages objectui#3696 fixed. It is baselined rather than fixed + * because objectui#3696 scoped itself to `packages/*` by name; objectui#3702 + * carries it, and fixing it means copying the root LICENSE and deleting the + * line below. + */ +const KNOWN_LICENSE_TEXT_MISSING: Record = { + 'apps/console': { issue: 'objectui#3702' }, +}; + +describe('published packages that declare a license ship its text (objectui#3696)', () => { + it('discovers the packages that owe license text (guard cannot pass by finding nothing)', () => { + // Same anti-vacuity floor as the guard above, sitting just under the 39 + // measured on main@3e601773e. A predicate that quietly matches nothing — + // a renamed `license` field, a lost workspace root, a `private` default + // flipped — would otherwise report success over an empty set. + expect(owingPackages.length).toBeGreaterThanOrEqual(38); + + // The two packages objectui#3696 fixed must be in scope BY NAME. If either + // drops out of the population, this guard has stopped watching the exact + // spot that motivated it, and the pin below would pass on a package nobody + // is checking any more. + const names = owingPackages.map((p) => p.name); + expect(names, '@object-ui/react-runtime must still be required to ship license text').toContain( + '@object-ui/react-runtime', + ); + expect(names, '@object-ui/sdui-parser must still be required to ship license text').toContain( + '@object-ui/sdui-parser', + ); + }); + + it('every package that declares a license has the text on disk', () => { + const violations = withoutLicenseText + .filter((p) => !Object.hasOwn(KNOWN_LICENSE_TEXT_MISSING, p.dir)) + .map((p) => `${p.name} (${p.dir}) declares "license": "${p.license}" but has no LICENSE file`); + + expect( + violations, + [ + 'A published package claims a license in package.json and ships no license text.', + 'The MIT terms require the licence and copyright notice to travel WITH the distribution,', + 'so the published tarball does not actually grant what its own manifest advertises', + '(objectui#3647/#3696).', + '', + 'npm packs LICENSE regardless of `files`, so the fix is the file itself, never a `files` entry:', + ' cp LICENSE /LICENSE # all 38 are byte-identical, blob cf2ca285', + '', + 'If the package should NOT be published, mark it `"private": true` instead — that is the', + 'other true direction, and it is why `object-ui` (the vscode-extension) is not listed here.', + '', + ...violations, + ].join('\n'), + ).toEqual([]); + }); + + it('the objectui#3696 license baseline only shrinks', () => { + // The other half of the ratchet, kept separate from the objectui#3663 one + // above so a stale entry names its own defect class. + const stillMissing = new Set(withoutLicenseText.map((p) => p.dir)); + const stale = Object.keys(KNOWN_LICENSE_TEXT_MISSING).filter((dir) => !stillMissing.has(dir)); + + expect( + stale, + [ + 'A KNOWN_LICENSE_TEXT_MISSING entry is stale: the package now ships license text, or it', + 'stopped owing any (it went `private`, or dropped its `license` field). Either way the', + 'defect is gone — delete its line from KNOWN_LICENSE_TEXT_MISSING to bank the progress.', + '', + ...stale.map((dir) => `${dir} (${KNOWN_LICENSE_TEXT_MISSING[dir].issue})`), + ].join('\n'), + ).toEqual([]); + }); + + it('pins the two packages fixed in objectui#3696', () => { + // The general assertion covers these, but naming them means a revert points + // straight at the issue that explains why the files have to be there — + // and, unlike the general assertion, this cannot be satisfied by adding a + // baseline line. + for (const dir of ['packages/react-runtime', 'packages/sdui-parser']) { + expect( + fs.existsSync(path.join(repoRoot, dir, 'LICENSE')), + `${dir}/LICENSE is required: the package declares "license": "MIT" and npm packs the file ` + + 'regardless of `files`, so deleting it silently resumes publishing MIT-labelled tarballs ' + + 'with no MIT text (objectui#3696).', + ).toBe(true); + } + }); + + it('requires license text only from packages that publish AND declare a license', () => { + // The predicate's truth table, asserted directly. Two of these four rows + // have no specimen in the tree today — every non-private package declares a + // license — so without this they would be untested logic that a later edit + // could invert with nothing turning red. + expect(owesLicenseText({ private: false, license: 'MIT' }), 'published + declared -> owes').toBe(true); + expect(owesLicenseText({ private: true, license: 'MIT' }), 'private -> makes no distribution').toBe(false); + expect(owesLicenseText({ private: false, license: undefined }), 'no claim -> nothing to honour').toBe(false); + expect(owesLicenseText({ private: false, license: ' ' }), 'blank claim is not a claim').toBe(false); + }); + + it('excludes private packages, and that exclusion is not vacuous', () => { + // The `private` limb only means something while some private package would + // otherwise be reported. Asserting the specimen set is NON-EMPTY first is + // what keeps this from becoming a test that passes because it examines + // nothing — today it is `object-ui` (packages/vscode-extension), which + // declares "MIT" and has no LICENSE file. + const privateOwingIfPublished = packages.filter( + (p) => p.private && p.license !== undefined && licenseTextFiles(p).length === 0, + ); + + expect( + privateOwingIfPublished.map((p) => p.name), + 'No private package declares a license without shipping its text any more, so this boundary ' + + 'case now proves nothing. Either pick a live specimen or delete this test — do not leave it ' + + 'green over an empty set.', + ).not.toEqual([]); + + const reported = new Set(withoutLicenseText.map((p) => p.name)); + for (const pkg of privateOwingIfPublished) { + expect(reported.has(pkg.name), `${pkg.name} is private and must not be required to ship license text`).toBe( + false, + ); + } + }); +}); From 430c0db87e0d655b937659615de28994e058b8d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 22:15:18 +0000 Subject: [PATCH 2/4] =?UTF-8?q?docs(scripts):=20=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=E9=97=A8=E7=A6=81=E6=B3=A8=E9=87=8A=E9=87=8C=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84=20issue=20=E4=BA=A4?= =?UTF-8?q?=E5=8F=89=E5=BC=95=E7=94=A8=20(#3696)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新块的注释里写了 "objectui#4984",但 objectui 的编号目前在 3700 一带, 该号不存在 —— 那是另一个仓的同族教训被误标成了本仓引用。悬空引用比没有引用 更坏:读者会去查一个查不到的单子。改为直接把道理写清楚(谓词的两肢今天在树上 没有标本,无人行经的逻辑可以被后来的改动反转而不转红),不再冒充交叉引用。 纯注释改动,断言与逻辑一字未动;11 passed 不变。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt --- scripts/__tests__/package-files-exist.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/__tests__/package-files-exist.test.ts b/scripts/__tests__/package-files-exist.test.ts index 1a0f4720c4..d73516c17a 100644 --- a/scripts/__tests__/package-files-exist.test.ts +++ b/scripts/__tests__/package-files-exist.test.ts @@ -415,8 +415,9 @@ function licenseTextFiles(pkg: WorkspacePackage): string[] { /** * Whether a package owes license text. Split out from the filter below so the * truth table can be asserted directly: two of its four cases have no specimen - * in the tree today, and an unexercised predicate limb is how objectui#4984's - * family of dead rules started. + * in the tree today (every non-private package declares a license), and a + * predicate limb no test ever walks is one a later edit can invert with nothing + * turning red. */ function owesLicenseText(pkg: Pick): boolean { return !pkg.private && pkg.license !== undefined && pkg.license.trim() !== ''; From ed34736adba4f792cdafba5e6c429a3694f7de0b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 23:38:19 +0000 Subject: [PATCH 3/4] =?UTF-8?q?test(scripts):=20license=20=E6=A3=98?= =?UTF-8?q?=E8=BD=AE=E7=9A=84=E9=99=88=E6=97=A7=E6=88=90=E5=9B=A0=E4=B9=9F?= =?UTF-8?q?=E6=8C=89=20#3701=20=E7=9A=84=E7=BA=A6=E5=AE=9A=E5=88=86?= =?UTF-8?q?=E5=8F=A5=E4=B8=8A=E6=8A=A5=20(#3696)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #3701(修 #3674)刚在同一文件里立了个约定:棘轮报陈旧时,成因要**逐条实测 上报**,不能由消息硬编码假定 —— 因为「离开基线」有多条路径,只有其中一条 意味着缺陷真被修好了,把假定写死会把读者引去核对一个并不成立的事实。 本 PR 的 license 棘轮有同样的结构,原消息虽然用的是「或」式并列(没有 #3674 那个断言错成因的毛病),但仍是让读者自己猜是哪一条。改成与 #3701 同形的 逐条成因,四条路径各自实测: 1. 现在有许可证文本了(并列出文件名)—— 只有这条意味着许可证真被补上 2. 改成了 private,不再分发,自然不欠 3. 不再声明 license 字段,没有主张要兑现 4. 该路径下已没有包(移走/删除,或这个基线键从来就没匹配上) 四条分支逐一实测,均按预测点名(证据见 PR 正文)。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt --- scripts/__tests__/package-files-exist.test.ts | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/scripts/__tests__/package-files-exist.test.ts b/scripts/__tests__/package-files-exist.test.ts index d73516c17a..3dfdc54ef3 100644 --- a/scripts/__tests__/package-files-exist.test.ts +++ b/scripts/__tests__/package-files-exist.test.ts @@ -497,14 +497,35 @@ describe('published packages that declare a license ship its text (objectui#3696 const stillMissing = new Set(withoutLicenseText.map((p) => p.dir)); const stale = Object.keys(KNOWN_LICENSE_TEXT_MISSING).filter((dir) => !stillMissing.has(dir)); + // WHY an entry stopped being a live defect, reported per entry rather than + // assumed — the convention objectui#3701 established for the baseline + // above, and for the same reason. An entry leaves this baseline by four + // routes and only the FIRST means someone added the licence: the other + // three mean the package stopped owing one, which is a different fact with + // a different reviewer response. Derived from the same data the predicate + // reads, so it cannot drift from the verdict it explains. + const causeOf = (dir: string): string => { + const pkg = packages.find((p) => p.dir === dir); + if (!pkg) + return 'no package sits at that path any more: it was moved or removed from the workspace, or this baseline key never matched one'; + const texts = licenseTextFiles(pkg); + if (texts.length > 0) return `it now ships license text (${texts.join(', ')})`; + if (pkg.private) return 'it is now `private`, so it distributes nothing and owes no text'; + return 'it no longer declares a `license` field, so there is no claim left to honour'; + }; + expect( stale, [ - 'A KNOWN_LICENSE_TEXT_MISSING entry is stale: the package now ships license text, or it', - 'stopped owing any (it went `private`, or dropped its `license` field). Either way the', - 'defect is gone — delete its line from KNOWN_LICENSE_TEXT_MISSING to bank the progress.', + 'A KNOWN_LICENSE_TEXT_MISSING entry no longer describes a live defect.', + 'Delete its line from KNOWN_LICENSE_TEXT_MISSING to bank the progress — that is the', + 'right move under every cause below.', + '', + 'The cause is reported per entry rather than assumed, because only one of the four', + 'routes out of this baseline means the licence was actually added; do not read a stale', + 'line as proof that the package now ships its terms.', '', - ...stale.map((dir) => `${dir} (${KNOWN_LICENSE_TEXT_MISSING[dir].issue})`), + ...stale.map((dir) => `${dir} (${KNOWN_LICENSE_TEXT_MISSING[dir].issue}) — ${causeOf(dir)}`), ].join('\n'), ).toEqual([]); }); From 86e51cc78359d6a887a590e7bb4725c610ebd2e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 23:43:48 +0000 Subject: [PATCH 4/4] =?UTF-8?q?fix(console):=20=E8=A1=A5=E4=B8=8A=20@objec?= =?UTF-8?q?t-ui/console=20=E7=9A=84=E8=AE=B8=E5=8F=AF=E8=AF=81=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E5=B9=B6=E6=8A=8A=E5=9F=BA=E7=BA=BF=E6=B8=85=E7=A9=BA?= =?UTF-8?q?=20(#3702)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PM 分诊裁定把 #3702 并入本 PR。@object-ui/console(apps/console)与 #3696 的 两个包是同一个缺陷:非 private、声明 "license": "MIT"、无许可证文本,而它确是 发布物(publishConfig.access: public、版本 17.3.0 同线、在 changeset fixed 组、 有 prepublishOnly)。之前它超出 #3696 的文件面,故只挂账不修。 改动两处: 1. apps/console/LICENSE —— 仓根 LICENSE 的逐字节副本,blob 同为 cf2ca28574caca42deafdee0ea935fc2e2823427。未动 files 字段(npm 恒定收录 LICENSE),npm pack --dry-run 清单 3 → 4 个文件,LICENSE 出现。 2. KNOWN_LICENSE_TEXT_MISSING 清空。删除前先跑了一次套件让棘轮报陈旧, 逐条成因命中第一条(唯一意味着许可证真被补上的那条): apps/console (objectui#3702) — it now ships license text (LICENSE) 删除后 11 passed。 同时: - 基线上方的文档注释从「现在时描述一个条目」改写为历史记录。留着不动就会变成 一份描述并不存在的条目的声明 —— 与本 PR 正在消除的失真同类,只是搬进了注释 (#3687 的同一处理)。棘轮机理段原文未动。 - 点名钉扎从两个包扩到三个,各带自己的 issue 号。这条比通用断言强:它不能靠 往基线里加一行来满足。删掉 apps/console/LICENSE 实测 2 红(通用断言 + 钉扎), 证明修完之后留下的是覆盖,不是沉默。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt --- apps/console/LICENSE | 21 +++++++++++ scripts/__tests__/package-files-exist.test.ts | 35 ++++++++++++------- 2 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 apps/console/LICENSE diff --git a/apps/console/LICENSE b/apps/console/LICENSE new file mode 100644 index 0000000000..cf2ca28574 --- /dev/null +++ b/apps/console/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 ObjectQL + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/scripts/__tests__/package-files-exist.test.ts b/scripts/__tests__/package-files-exist.test.ts index 3dfdc54ef3..ac71e816d2 100644 --- a/scripts/__tests__/package-files-exist.test.ts +++ b/scripts/__tests__/package-files-exist.test.ts @@ -434,17 +434,18 @@ const withoutLicenseText = owingPackages.filter((p) => licenseTextFiles(p).lengt * without consulting this map, and an entry here that is no longer offending * fails too, so it can only shrink. * - * `apps/console` (`@object-ui/console`) is the one entry. It is published for - * real — `publishConfig.access: "public"`, versioned 17.3.0 with the rest, in - * the `.changeset/config.json` `fixed` group — and it has the identical defect - * to the two packages objectui#3696 fixed. It is baselined rather than fixed - * because objectui#3696 scoped itself to `packages/*` by name; objectui#3702 - * carries it, and fixing it means copying the root LICENSE and deleting the - * line below. + * Currently empty, which is the intended resting state — the ratchet still + * fails on any NEW offender. It was written carrying one entry, + * `apps/console` (`@object-ui/console`): published for real + * (`publishConfig.access: "public"`, versioned 17.3.0 with the rest, in the + * `.changeset/config.json` `fixed` group) with the identical defect to the two + * packages objectui#3696 fixed, but outside that issue's `packages/*` scope, so + * it was booked to objectui#3702 instead of fixed silently. Triage folded + * objectui#3702 into the same PR, the root LICENSE was copied to + * `apps/console/LICENSE`, and the entry was banked — the ratchet reported it + * stale first, naming the cause (`it now ships license text (LICENSE)`). */ -const KNOWN_LICENSE_TEXT_MISSING: Record = { - 'apps/console': { issue: 'objectui#3702' }, -}; +const KNOWN_LICENSE_TEXT_MISSING: Record = {}; describe('published packages that declare a license ship its text (objectui#3696)', () => { it('discovers the packages that owe license text (guard cannot pass by finding nothing)', () => { @@ -530,17 +531,25 @@ describe('published packages that declare a license ship its text (objectui#3696 ).toEqual([]); }); - it('pins the two packages fixed in objectui#3696', () => { + it('pins the three packages fixed in objectui#3696 / objectui#3702', () => { // The general assertion covers these, but naming them means a revert points // straight at the issue that explains why the files have to be there — // and, unlike the general assertion, this cannot be satisfied by adding a // baseline line. - for (const dir of ['packages/react-runtime', 'packages/sdui-parser']) { + const fixed: Record = { + 'packages/react-runtime': 'objectui#3696', + 'packages/sdui-parser': 'objectui#3696', + // Found by re-running objectui#3696's census over ALL FOUR workspace + // roots instead of `packages/*` alone, which is how it was missed twice. + 'apps/console': 'objectui#3702', + }; + + for (const [dir, issue] of Object.entries(fixed)) { expect( fs.existsSync(path.join(repoRoot, dir, 'LICENSE')), `${dir}/LICENSE is required: the package declares "license": "MIT" and npm packs the file ` + 'regardless of `files`, so deleting it silently resumes publishing MIT-labelled tarballs ' + - 'with no MIT text (objectui#3696).', + `with no MIT text (${issue}).`, ).toBe(true); } });