From ab6529d96801e39e9d7048b0dd928bea2f173b24 Mon Sep 17 00:00:00 2001 From: lick <2188718831@qq.com> Date: Fri, 13 Nov 2026 19:27:16 -0800 Subject: [PATCH] release: include and verify project and Rust dependency notices in Java 1.1.2 --- .github/workflows/ci.yml | 8 ++ .github/workflows/release.yml | 5 + NOTICE | 4 + README.md | 10 +- docs/releases/v1.1.2.md | 57 +++++++++ io.github.lithe-idea.java-support/plugin.json | 2 +- scripts/assemble-package.sh | 3 + scripts/package/build-windows.mjs | 3 + scripts/package/collect-notices.mjs | 121 ++++++++++++++++++ scripts/package/collect-notices.test.mjs | 51 ++++++++ scripts/verify-package.sh | 7 +- third_party/rust-notices/manifest.json | 18 +++ .../rust-notices/tree-sitter-0.25.10.LICENSE | 21 +++ .../tree-sitter-java-0.23.5.LICENSE | 21 +++ 14 files changed, 326 insertions(+), 5 deletions(-) create mode 100644 docs/releases/v1.1.2.md create mode 100644 scripts/package/collect-notices.mjs create mode 100644 scripts/package/collect-notices.test.mjs create mode 100644 third_party/rust-notices/manifest.json create mode 100644 third_party/rust-notices/tree-sitter-0.25.10.LICENSE create mode 100644 third_party/rust-notices/tree-sitter-java-0.23.5.LICENSE diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bd0031..4d61c03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,11 @@ jobs: with: components: rustfmt, clippy - uses: Swatinem/rust-cache@v2 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Test package notices + run: node --test --test-timeout=60000 scripts/package/collect-notices.test.mjs - name: Check formatting run: cargo fmt --all --check - name: Lint @@ -46,6 +51,9 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false + - uses: actions/setup-node@v4 + with: + node-version: 22 # scripts/assemble-package.sh compiles the plugin backend for each # architecture it packages. - uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c76c255..94d504b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,11 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy + - uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Test package notices + run: node --test --test-timeout=60000 scripts/package/collect-notices.test.mjs - name: Check formatting run: cargo fmt --all --check - name: Lint diff --git a/NOTICE b/NOTICE index 5f36415..a96b945 100644 --- a/NOTICE +++ b/NOTICE @@ -42,3 +42,7 @@ here from the Lithe repository; the file names its origin. Lithe is not affiliated with OpenIDEA, JetBrains s.r.o., the Eclipse Foundation, Microsoft or their products. + +Rust build dependency license texts and a per-file SHA-256 manifest are +provided under licenses/rust/ in distributed plugin archives. The manifest +records the locked crate names, versions and license expressions. diff --git a/README.md b/README.md index 9dc3069..e447051 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Java Support for Lithe -> **Viva 预览版 Java 支持(1.1.1)**:清单已迁到当前契约 0,后端使用 Core +> **Viva 预览版 Java 支持(1.1.2)**:清单已迁到当前契约 0,后端使用 Core > `65cf82343c5e315aae2e335b2e8f0217b703e89a` 中的 Rust SDK 0.4.0(尚无对应发布标签)。 > 本轮验证含第三方 JAR 依赖的普通 Maven 工程:JDT 补全、Maven 编译、main 运行和断点调试,包含变量读取、继续和退出。 > 需要同时包含 Lithe 客户端的动作注册解码、动作刷新与首次调试加载修复;未修改这些行为的旧 preview 不能直接使用本包。 @@ -24,7 +24,7 @@ > SDK. Maven and Gradle import settings come from the Maven and Gradle plugins through a function interface that > Lithe Core relays between plugins. -1.1.1 面向包含主仓库 [#1884](https://github.com/1lck/Lithe-IDEA/pull/1884) 的配套 Viva preview(客户端修订 `607f00967d70a7017d49805183d0d2e6ffdf1c0d` 或更新版本)。API / 能力与传输版本以 `plugin.json`、`sdk.lock.json` +1.1.2 面向包含主仓库 [#1884](https://github.com/1lck/Lithe-IDEA/pull/1884) 的配套 Viva preview(客户端修订 `607f00967d70a7017d49805183d0d2e6ffdf1c0d` 或更新版本)。API / 能力与传输版本以 `plugin.json`、`sdk.lock.json` 和固定 Core 中的[插件资料](https://github.com/Lithe-IDEA/Lithe-core-rust/tree/65cf82343c5e315aae2e335b2e8f0217b703e89a/shared/plugins)为准。 `MIGRATION.md` 保留迁移历史,不能用其中旧宿主、旧 API 数字推断当前兼容性。 @@ -233,7 +233,7 @@ scripts/ # 组装、校验、归档插件包,检查 SD MIGRATION.md # 主仓库路径到本仓库的映射、待宿主接口清单、主仓库删除清单 ``` -Rust 部分需要 Rust 工具链和 Cargo;打包脚本需要 macOS、`zsh`、`curl`、`ditto`,不需要 Xcode 或 Swift 工具链。 +Rust 部分需要 Rust 工具链和 Cargo;打包脚本需要 macOS、Node.js 22、`zsh`、`curl`、`ditto`,不需要 Xcode 或 Swift 工具链。 打包脚本会用 `cargo build --release --locked -p lithe-java-backend --target ` 编译后端,所以还要安装两个 Apple target: `rustup target add aarch64-apple-darwin x86_64-apple-darwin`(`--arch arm64` 用前者,`x86_64` 用后者,`universal` 两个都编译再用 `lipo -create` 合并)。 @@ -280,3 +280,7 @@ node scripts/package/build-windows.mjs /path/to/lithe-java-backend.exe ## 许可 插件自身的代码按 Apache License 2.0 发布(见 `LICENSE`)。插件包内的第三方组件各自保留原许可,见 `NOTICE`。 + +### 随包许可 + +两端构建都会复制自身 LICENSE/NOTICE,并用 Cargo 锁定的非 dev 构建图收集 Rust 依赖的许可,写入 licenses/rust 和不含本机路径的哈希清单。SDK 沿其 Git 检出查找共享许可;crate 未携带主许可时,只接受 third_party/rust-notices 中与 crate VCS 修订及 SHA-256 匹配的固定文本。tree-sitter 内嵌的 Unicode 许可一并保留。缺少许可或来源不匹配会终止打包;不在运行期下载,不跨工作树共享生成目录。 diff --git a/docs/releases/v1.1.2.md b/docs/releases/v1.1.2.md new file mode 100644 index 0000000..e2dd509 --- /dev/null +++ b/docs/releases/v1.1.2.md @@ -0,0 +1,57 @@ +## 中文 + +Java Support 1.1.2 为配套 Viva 预览版提供 Java 项目支持。本版本是预发布,插件包不带发布者签名。 + +### 下载与安装 + +下载本页对应系统的 ZIP:macOS Apple Silicon(`darwin-arm64`)、macOS Intel(`darwin-x64`)或 Windows x64(`windows-x64`)。在兼容 Viva 客户端的插件设置中选择从文件安装 ZIP。 + +需要包含 [Lithe #1884](https://github.com/1lck/Lithe-IDEA/pull/1884) 的客户端(修订 `607f00967d70a7017d49805183d0d2e6ffdf1c0d` 或更新版本)及 Core #29。旧版 macOS Lithe 和未包含这些修复的 Viva preview 不受支持。插件使用契约/API/能力版本 0;语言服务器使用随包 Temurin 21,项目运行仍使用用户选择的 JDK。 + +Maven 项目配合 Maven 0.0.1;Gradle 项目配合 Gradle 1.0.0。构建工具由用户安装或由项目 Wrapper 提供。 + +### 更新 + +- 补齐自身 LICENSE/NOTICE 与 Rust 构建依赖的许可和哈希清单,缺失时阻止打包。 + +- 跑通普通 Maven、Gradle 和普通 Java 目录的补全、编译、运行及断点调试,Java 8 项目保留正确的编译目标。 +- Windows 后端静态链接编译器运行库,发布检查拒绝额外的 VC++/MinGW 运行库依赖。 +- 修复 Windows 可执行路径、中文/UNC 文件 URI、Lombok 加载及过长 classpath;退出后清理临时 classpath JAR。 +- 冷索引暂时无法返回运行动作时,在索引任务完成后有界恢复,不增加后台轮询。 +- 已在 macOS 与 Windows 无头验证断点变量、继续/停止、编译错误阻止运行旧 class、暂停时关闭及插件安装目录只读。 + +### 兼容性与已知限制 + +尚未完整验收原生窗口、全部日志/取消路径、JUnit/TestNG 行内运行器和 Spring/MyBatis。JSP HTTP/Servlet 调试通过不代表 JSP 源码补全或 JSP 断点;Ant 源码布局不代表执行 Ant target。Java 5/7 源码级别、长 module-path、JavaCC 生成模型、多版本 JAR 和部分旧泛型项目仍有限制,详见 README 与主仓库 #1881–1883。 + +实测 JDT 内存与补全延迟没有显著改善,本版本不宣称整体性能提升。Windows 的真实工作流在 ARM Windows 上使用 x64 工具验证,其耗时不代表原生 AMD64 性能。 + +--- + +## English + +Java Support 1.1.2 targets the matching Viva preview. This is a prerelease with unsigned plugin archives. + +### Download and installation + +Choose the ZIP for macOS Apple Silicon (`darwin-arm64`), macOS Intel (`darwin-x64`), or Windows x64 (`windows-x64`), then install it from a file in the compatible Viva client's plugin settings. + +The client must include [Lithe #1884](https://github.com/1lck/Lithe-IDEA/pull/1884), revision `607f00967d70a7017d49805183d0d2e6ffdf1c0d` or newer, and Core #29. Legacy macOS Lithe and earlier Viva previews are unsupported. Contract, API and capability versions are 0. Bundled Temurin 21 runs the language server; project execution uses the selected project JDK. + +Use Maven 0.0.1 for Maven projects and Gradle 1.0.0 for Gradle projects. Supply the build tool yourself or through the project's wrapper. + +### Changes + +- Include project and Rust dependency notices with a hash manifest; missing notices stop packaging. + +- Validated completion, compilation, execution and breakpoint debugging for ordinary Maven, Gradle and plain Java projects, preserving Java 8 compilation targets. +- The Windows backend statically links compiler runtimes; release checks reject extra VC++/MinGW runtime DLL dependencies. +- Fixed Windows executable paths, Unicode/UNC file URIs, Lombok loading and long classpaths; temporary classpath JARs are removed after execution. +- Recover run actions after cold-index jobs finish, with bounded retries and no background polling. +- Headless macOS and Windows workflows cover breakpoint locals, continue/stop, refusal to run stale classes after compilation errors, closing paused sessions and read-only installed packages. + +### Compatibility and known limitations + +Native windows, all logging/cancellation paths, inline JUnit/TestNG and Spring/MyBatis have not been fully accepted. JSP HTTP/Servlet debugging does not cover JSP-source completion or JSP breakpoints; Ant source layouts do not cover Ant targets. Java 5/7 source levels, long module-paths, JavaCC models, multi-release JARs and some older generic code remain limited; see README and Lithe #1881–1883. + +Measurements found no significant JDT memory or completion-latency improvement. Windows workflows used x64 tools on ARM Windows, so their timings do not represent native AMD64 performance. diff --git a/io.github.lithe-idea.java-support/plugin.json b/io.github.lithe-idea.java-support/plugin.json index cb79fda..d246975 100644 --- a/io.github.lithe-idea.java-support/plugin.json +++ b/io.github.lithe-idea.java-support/plugin.json @@ -2,7 +2,7 @@ "schemaVersion": 0, "id": "io.github.lithe-idea.java-support", "displayName": "Java Support", - "version": "1.1.1", + "version": "1.1.2", "pluginAPIVersion": 0, "requires": {"backend": 0, "debugAdapters": 0, "languages": 0, "languageServers": 0, "pluginFunctions": 0, "settings": 0, "projectGenerators": 0, "toolDefinitions": 0}, "requiresTools": ["java"], diff --git a/scripts/assemble-package.sh b/scripts/assemble-package.sh index 6a2d2e5..849f5eb 100755 --- a/scripts/assemble-package.sh +++ b/scripts/assemble-package.sh @@ -159,3 +159,6 @@ if [[ -n "$(find "$OUTPUT_DIR" -type l -print -quit)" ]]; then print -u2 -- "The Java Support package contains symbolic links: $(find "$OUTPUT_DIR" -type l -print -quit)" exit 1 fi + +# Notices are build inputs, copied alongside the binary from locked sources. +node "$ROOT_DIR/scripts/package/collect-notices.mjs" "$OUTPUT_DIR" >&2 diff --git a/scripts/package/build-windows.mjs b/scripts/package/build-windows.mjs index fd68d5c..5c722e8 100644 --- a/scripts/package/build-windows.mjs +++ b/scripts/package/build-windows.mjs @@ -11,6 +11,7 @@ import { fileURLToPath } from 'node:url'; import { execFileSync } from 'node:child_process'; import { Readable } from 'node:stream'; import { pipeline } from 'node:stream/promises'; +import { collectNotices, verifyNotices } from './collect-notices.mjs'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..'); if (process.platform !== 'darwin') throw new Error('Assemble this development package on macOS.'); @@ -86,6 +87,8 @@ try { await writeFile(join(packageRoot, 'plugin.json'), `${JSON.stringify(manifest, null, 2)}\n`); await mkdir(join(packageRoot, 'bin')); await cp(resolve(backend), join(packageRoot, manifest.backend.launch.path)); + collectNotices(packageRoot); + verifyNotices(packageRoot); const target = join(artifacts, `Lithe-Java-Support-${manifest.version}-windows-x64.zip`); execFileSync('/usr/bin/ditto', ['-c', '-k', '--keepParent', packageRoot, target], { timeout: 120_000 }); await writeFile(`${target}.sha256`, `${await digest(target)} ${target.split('/').at(-1)}\n`); diff --git a/scripts/package/collect-notices.mjs b/scripts/package/collect-notices.mjs new file mode 100644 index 0000000..b1732ef --- /dev/null +++ b/scripts/package/collect-notices.mjs @@ -0,0 +1,121 @@ +#!/usr/bin/env node +// Copy notices from Cargo's locked build graph. No runtime download or source +// modification: registry/Git inputs stay owned by Cargo, output by the package. +import {execFileSync} from 'node:child_process'; +import {createHash} from 'node:crypto'; +import {existsSync,readFileSync,readdirSync,lstatSync,mkdirSync,writeFileSync,chmodSync,rmSync} from 'node:fs'; +import {dirname,isAbsolute,join,resolve} from 'node:path'; +import {fileURLToPath} from 'node:url'; + +const repository=resolve(dirname(fileURLToPath(import.meta.url)),'../..'); +const sha=data=>createHash('sha256').update(data).digest('hex'); +const isNotice=name=>/^(LICENSE|COPYING|NOTICE)(?:[._-]|$)/i.test(name); +const isLicense=name=>/^(LICENSE|COPYING)(?:[._-]|$)/i.test(name); +function documents(directory,recursive=false,prefix='') { + return readdirSync(directory,{withFileTypes:true}).flatMap(entry=>{ + const path=join(prefix,entry.name); + if(entry.isDirectory() && recursive) return documents(join(directory,entry.name),true,path); + return entry.isFile() && isNotice(entry.name) ? [path] : []; + }).sort(); +} +function readDocument(path) { + if(!lstatSync(path).isFile() || lstatSync(path).size>1024*1024) throw Error('Invalid or oversized notice'); + return readFileSync(path); +} + +export function collectNotices(packageRoot,metadata) { + packageRoot=resolve(packageRoot); + const manifest=JSON.parse(readFileSync(join(packageRoot,'plugin.json'),'utf8')); + if(manifest.id!=='io.github.lithe-idea.java-support') throw Error('A Java Support package is required'); + metadata??=JSON.parse(execFileSync('cargo',['metadata','--locked','--format-version','1'],{ + cwd:repository,encoding:'utf8',timeout:180000,maxBuffer:16*1024*1024, + })); + const nodes=new Map(metadata.resolve.nodes.map(node=>[node.id,node])); + const backend=metadata.packages.find(pkg=>pkg.name==='lithe-java-backend'); + if(!backend) throw Error('Missing backend build graph'); + const selected=new Set(); + function visit(id) { + if(selected.has(id)) return; + selected.add(id); + const node=nodes.get(id); + if(!node) throw Error('Incomplete Cargo graph'); + for(const dep of node.deps) if(dep.dep_kinds.some(kind=>kind.kind!=='dev')) visit(dep.pkg); + } + visit(backend.id); + const fallbacks=JSON.parse(readFileSync(join(repository,'third_party/rust-notices/manifest.json'),'utf8')); + const destination=join(packageRoot,'licenses/rust'); + rmSync(destination,{recursive:true,force:true}); + mkdirSync(destination,{recursive:true}); + for(const name of ['LICENSE','NOTICE']) { + writeFileSync(join(packageRoot,name),readDocument(join(metadata.workspace_root,name)),{mode:0o644}); + chmodSync(join(packageRoot,name),0o644); + } + const records=[]; + for(const pkg of metadata.packages.filter(pkg=>selected.has(pkg.id)).sort((a,b)=>`${a.name}@${a.version}`<`${b.name}@${b.version}`?-1:1)) { + const key=`${pkg.name}-${pkg.version}`; + if(!/^[a-zA-Z0-9_.+-]+$/.test(key)) throw Error('Invalid Cargo package identity'); + const source=dirname(pkg.manifest_path); + let primary=source; + if(metadata.workspace_members.includes(pkg.id)) primary=metadata.workspace_root; + let names=documents(primary); + if(!names.some(isLicense) && pkg.source?.startsWith('git+')) { + // SDKs may inherit a shared license above their crate. Stop at the Git + // checkout boundary rather than accepting a license from the user home. + for(let level=0;level<8;level++) { + if(existsSync(join(primary,'.git')) || dirname(primary)===primary) break; + primary=dirname(primary); names=documents(primary); + if(names.some(isLicense)) break; + } + } + const files=[]; + function copy(name,data) { + const target=join(destination,key,name); + mkdirSync(dirname(target),{recursive:true}); + writeFileSync(target,data,{mode:0o644}); chmodSync(target,0o644); + files.push({path:`${key}/${name.split('\\').join('/')}`,sha256:sha(data)}); + } + if(names.some(isLicense)) { + for(const name of names) copy(name,readDocument(join(primary,name))); + } else { + const fallback=fallbacks.find(item=>item.name===pkg.name && item.version===pkg.version); + if(!fallback) throw Error(`Missing primary license for ${key}`); + const vcs=JSON.parse(readFileSync(join(source,'.cargo_vcs_info.json'),'utf8')); + if(vcs.git.sha1!==fallback.revision) throw Error(`License source revision mismatch for ${key}`); + const data=readDocument(join(repository,'third_party/rust-notices',fallback.file)); + if(sha(data)!==fallback.sha256) throw Error(`License hash mismatch for ${key}`); + copy('LICENSE',data); + } + // Retain embedded notices too, such as tree-sitter's Unicode license. + for(const name of metadata.workspace_members.includes(pkg.id)?[]:documents(source,true)) { + if(primary===source && names.includes(name)) continue; + copy(join('source',name),readDocument(join(source,name))); + } + records.push({name:pkg.name,version:pkg.version,license:pkg.license,files}); + } + writeFileSync(join(destination,'manifest.json'),JSON.stringify({schemaVersion:1,packages:records},null,2)+'\n'); + return records; +} +export function verifyNotices(packageRoot) { + for(const name of ['LICENSE','NOTICE']) { + if(!readDocument(join(packageRoot,name)).equals(readDocument(join(repository,name)))) throw Error(`Missing or modified ${name}`); + } + const root=join(packageRoot,'licenses/rust'); + const manifest=JSON.parse(readDocument(join(root,'manifest.json')).toString('utf8')); + if(manifest.schemaVersion!==1 || !manifest.packages?.length) throw Error('Missing Rust build notices'); + for(const pkg of manifest.packages) { + if(!pkg.files?.length) throw Error('Missing dependency notice files'); + for(const file of pkg.files) { + if(typeof file.path!=='string' || isAbsolute(file.path) || file.path.split(/[\\/]/).includes('..')) throw Error('Unsafe notice path'); + if(sha(readDocument(join(root,file.path)))!==file.sha256) throw Error('Modified dependency notice'); + } + } +} +if(process.argv[1] && resolve(process.argv[1])===fileURLToPath(import.meta.url)) { + if(process.argv.length===4 && process.argv[2]==='--verify') verifyNotices(process.argv[3]); + else { + if(process.argv.length!==3) throw Error('Usage: collect-notices.mjs [--verify] '); + const records=collectNotices(process.argv[2]); + verifyNotices(process.argv[2]); + process.stderr.write(`Collected locked Rust notices for ${records.length} build packages\n`); + } +} diff --git a/scripts/package/collect-notices.test.mjs b/scripts/package/collect-notices.test.mjs new file mode 100644 index 0000000..f75ebb8 --- /dev/null +++ b/scripts/package/collect-notices.test.mjs @@ -0,0 +1,51 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {mkdtempSync,mkdirSync,writeFileSync,readFileSync,rmSync} from 'node:fs'; +import {tmpdir} from 'node:os'; +import {join} from 'node:path'; +import {createHash} from 'node:crypto'; +import {collectNotices,verifyNotices} from './collect-notices.mjs'; + +function fixture(t) { + const root=mkdtempSync(join(tmpdir(),'java-notices-')); + t.after(()=>rmSync(root,{recursive:true,force:true})); + const output=join(root,'output'); mkdirSync(output); + writeFileSync(join(output,'plugin.json'),JSON.stringify({id:'io.github.lithe-idea.java-support'})); + writeFileSync(join(root,'LICENSE'),readFileSync(new URL('../../LICENSE',import.meta.url)));writeFileSync(join(root,'NOTICE'),readFileSync(new URL('../../NOTICE',import.meta.url))); + const packages=[{id:'app',name:'lithe-java-backend',version:'0.1.0',manifest_path:join(root,'Cargo.toml'),source:null,license:'Apache-2.0'}]; + const metadata={workspace_root:root,workspace_members:['app'],packages,resolve:{nodes:[{id:'app',deps:[]}]}}; + return {root,output,metadata}; +} +test('copies notices and hashes normal build dependencies while excluding dev-only packages',t=>{ + const f=fixture(t);const dep=join(f.root,'dep');mkdirSync(dep); + writeFileSync(join(dep,'LICENSE-MIT'),'dependency license'); + f.metadata.packages.push({id:'dep',name:'dependency',version:'1.0.0',manifest_path:join(dep,'Cargo.toml'),source:'registry+test',license:'MIT'}); + f.metadata.packages.push({id:'dev',name:'test-only',version:'1.0.0',manifest_path:join(f.root,'absent','Cargo.toml'),source:'registry+test',license:'MIT'}); + f.metadata.resolve.nodes[0].deps=[{pkg:'dep',dep_kinds:[{kind:null}]},{pkg:'dev',dep_kinds:[{kind:'dev'}]}]; + f.metadata.resolve.nodes.push({id:'dep',deps:[]}); + const records=collectNotices(f.output,f.metadata); + assert.deepEqual(records.map(p=>p.name),['dependency','lithe-java-backend']); + assert.equal(readFileSync(join(f.output,'NOTICE'),'utf8'),readFileSync(new URL('../../NOTICE',import.meta.url),'utf8')); + const doc=records[0].files[0]; + assert.equal(doc.sha256,createHash('sha256').update('dependency license').digest('hex')); + assert.equal(readFileSync(join(f.output,'licenses/rust',doc.path),'utf8'),'dependency license'); + assert.equal(JSON.stringify(records).includes(f.root),false); + verifyNotices(f.output); + writeFileSync(join(f.output,'licenses/rust',doc.path),'tampered'); + assert.throws(()=>verifyNotices(f.output),/Modified dependency notice/); +}); +test('fails rather than omitting an unknown dependency without a primary license',t=>{ + const f=fixture(t);const dep=join(f.root,'missing');mkdirSync(dep); + f.metadata.packages.push({id:'missing',name:'missing',version:'1.0.0',manifest_path:join(dep,'Cargo.toml'),source:'registry+test',license:'MIT'}); + f.metadata.resolve.nodes[0].deps=[{pkg:'missing',dep_kinds:[{kind:null}]}];f.metadata.resolve.nodes.push({id:'missing',deps:[]}); + assert.throws(()=>collectNotices(f.output,f.metadata),/Missing primary license for missing-1.0.0/); +}); +test('retains nested notices in addition to the primary license',t=>{ + const f=fixture(t);const dep=join(f.root,'dep');mkdirSync(join(dep,'unicode'),{recursive:true}); + writeFileSync(join(dep,'LICENSE'),'MIT');writeFileSync(join(dep,'unicode/LICENSE'),'Unicode'); + f.metadata.packages.push({id:'dep',name:'dependency',version:'1.0.0',manifest_path:join(dep,'Cargo.toml'),source:'registry+test',license:'MIT'}); + f.metadata.resolve.nodes[0].deps=[{pkg:'dep',dep_kinds:[{kind:'build'}]}];f.metadata.resolve.nodes.push({id:'dep',deps:[]}); + const records=collectNotices(f.output,f.metadata); + assert.equal(records[0].files.length,2); + assert.equal(readFileSync(join(f.output,'licenses/rust/dependency-1.0.0/source/unicode/LICENSE'),'utf8'),'Unicode'); +}); diff --git a/scripts/verify-package.sh b/scripts/verify-package.sh index d78b76d..8bd8323 100755 --- a/scripts/verify-package.sh +++ b/scripts/verify-package.sh @@ -48,7 +48,12 @@ cmp -s "$manifest" "$ROOT_DIR/$PLUGIN_ID/plugin.json" || [[ "${PACKAGE:A:t}" == "$PLUGIN_ID" ]] || fail "the package directory must be named $PLUGIN_ID" -allowed=(plugin.json bin icons templates language-server runtime) +for notice in LICENSE NOTICE; do + cmp -s "$PACKAGE/$notice" "$ROOT_DIR/$notice" || fail "missing or modified $notice" +done +node "$ROOT_DIR/scripts/package/collect-notices.mjs" --verify "$PACKAGE" + +allowed=(plugin.json bin icons templates language-server runtime LICENSE NOTICE licenses) for entry in "$PACKAGE"/*(DN); do (( ${allowed[(Ie)${entry:t}]} )) || fail "undeclared top-level entry ${entry:t}" done diff --git a/third_party/rust-notices/manifest.json b/third_party/rust-notices/manifest.json new file mode 100644 index 0000000..c7137b6 --- /dev/null +++ b/third_party/rust-notices/manifest.json @@ -0,0 +1,18 @@ +[ + { + "name": "tree-sitter", + "version": "0.25.10", + "revision": "da6fe9beb4f7f67beb75914ca8e0d48ae48d6406", + "url": "https://raw.githubusercontent.com/tree-sitter/tree-sitter/da6fe9beb4f7f67beb75914ca8e0d48ae48d6406/LICENSE", + "file": "tree-sitter-0.25.10.LICENSE", + "sha256": "5f9cf9fb6acb1972b35ae29119ce563bb60ec097656bc4b69b9bac2d04c7a147" + }, + { + "name": "tree-sitter-java", + "version": "0.23.5", + "revision": "94703d5a6bed02b98e438d7cad1136c01a60ba2c", + "url": "https://raw.githubusercontent.com/tree-sitter/tree-sitter-java/94703d5a6bed02b98e438d7cad1136c01a60ba2c/LICENSE", + "file": "tree-sitter-java-0.23.5.LICENSE", + "sha256": "52ed137b039cd9c46409bc22e89938af911c95b157feae2d040b51e6084369a7" + } +] diff --git a/third_party/rust-notices/tree-sitter-0.25.10.LICENSE b/third_party/rust-notices/tree-sitter-0.25.10.LICENSE new file mode 100644 index 0000000..451fe1d --- /dev/null +++ b/third_party/rust-notices/tree-sitter-0.25.10.LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018-2024 Max Brunsfeld + +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/third_party/rust-notices/tree-sitter-java-0.23.5.LICENSE b/third_party/rust-notices/tree-sitter-java-0.23.5.LICENSE new file mode 100644 index 0000000..4e0446f --- /dev/null +++ b/third_party/rust-notices/tree-sitter-java-0.23.5.LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Ayman Nadeem + +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.