feat: add installable Lingji Android shell
This commit is contained in:
@@ -17,19 +17,42 @@ if (manifest.schemaVersion !== 1) failures.push("manifest schemaVersion must be
|
||||
if (!Array.isArray(manifest.components) || manifest.components.length === 0) {
|
||||
failures.push("manifest components must not be empty");
|
||||
}
|
||||
if (!Array.isArray(manifest.groupPolicies) || manifest.groupPolicies.length === 0) {
|
||||
failures.push("manifest groupPolicies must not be empty");
|
||||
}
|
||||
|
||||
const allowedLicenses = new Set(["Apache-2.0", "BSD-3-Clause", "EPL-1.0"]);
|
||||
const ids = new Set();
|
||||
const coordinates = new Set();
|
||||
for (const [index, component] of (manifest.components ?? []).entries()) {
|
||||
const prefix = `components[${index}]`;
|
||||
for (const field of ["id", "version", "scope", "license", "source"]) {
|
||||
if (typeof component[field] !== "string" || component[field].trim() === "") {
|
||||
const validateCommonFields = (entry, prefix) => {
|
||||
for (const field of ["id", "scope", "license", "source"]) {
|
||||
if (typeof entry[field] !== "string" || entry[field].trim() === "") {
|
||||
failures.push(`${prefix}.${field} must be non-blank text`);
|
||||
}
|
||||
}
|
||||
if (ids.has(component.id)) failures.push(`${prefix}.id must be unique`);
|
||||
ids.add(component.id);
|
||||
if (ids.has(entry.id)) failures.push(`${prefix}.id must be unique`);
|
||||
ids.add(entry.id);
|
||||
if (!allowedLicenses.has(entry.license)) failures.push(`${prefix}.license is not reviewed`);
|
||||
try {
|
||||
const url = new URL(entry.source);
|
||||
if (url.protocol !== "https:") throw new Error("not HTTPS");
|
||||
} catch {
|
||||
failures.push(`${prefix}.source must be an absolute HTTPS URL`);
|
||||
}
|
||||
if (!documentation.includes(entry.id) || !documentation.includes(entry.license)) {
|
||||
failures.push(`${prefix} is not represented in docs/dependency-licenses.md`);
|
||||
}
|
||||
};
|
||||
|
||||
for (const [index, component] of (manifest.components ?? []).entries()) {
|
||||
const prefix = `components[${index}]`;
|
||||
validateCommonFields(component, prefix);
|
||||
if (typeof component.version !== "string" || component.version.trim() === "") {
|
||||
failures.push(`${prefix}.version must be non-blank text`);
|
||||
}
|
||||
if (!documentation.includes(component.version)) {
|
||||
failures.push(`${prefix}.version is not represented in docs/dependency-licenses.md`);
|
||||
}
|
||||
if (component.coordinates !== null) {
|
||||
if (typeof component.coordinates !== "string" || component.coordinates.split(":").length !== 3) {
|
||||
failures.push(`${prefix}.coordinates must be group:name:version or null`);
|
||||
@@ -38,24 +61,31 @@ for (const [index, component] of (manifest.components ?? []).entries()) {
|
||||
}
|
||||
coordinates.add(component.coordinates);
|
||||
}
|
||||
if (!allowedLicenses.has(component.license)) failures.push(`${prefix}.license is not reviewed`);
|
||||
try {
|
||||
const url = new URL(component.source);
|
||||
if (url.protocol !== "https:") throw new Error("not HTTPS");
|
||||
} catch {
|
||||
failures.push(`${prefix}.source must be an absolute HTTPS URL`);
|
||||
}
|
||||
if (!documentation.includes(component.id) ||
|
||||
!documentation.includes(component.version) ||
|
||||
!documentation.includes(component.license)) {
|
||||
failures.push(`${prefix} is not represented in docs/dependency-licenses.md`);
|
||||
}
|
||||
|
||||
const groupPrefixes = new Set();
|
||||
for (const [index, policy] of (manifest.groupPolicies ?? []).entries()) {
|
||||
const prefix = `groupPolicies[${index}]`;
|
||||
validateCommonFields(policy, prefix);
|
||||
if (typeof policy.groupPrefix !== "string" || !/^[a-z][a-z0-9.]+$/u.test(policy.groupPrefix)) {
|
||||
failures.push(`${prefix}.groupPrefix must be a Maven group prefix`);
|
||||
} else if (groupPrefixes.has(policy.groupPrefix)) {
|
||||
failures.push(`${prefix}.groupPrefix must be unique`);
|
||||
}
|
||||
groupPrefixes.add(policy.groupPrefix);
|
||||
}
|
||||
|
||||
const version = (name) => new RegExp(`^${name}\\s*=\\s*"([^"]+)"`, "mu").exec(catalog)?.[1];
|
||||
const expectedVersions = new Map([
|
||||
["gradle-wrapper", /gradle-([\d.]+)-bin\.zip/u.exec(wrapper)?.[1]],
|
||||
["android-gradle-plugin", version("agp")],
|
||||
["kotlin-gradle-plugin", version("kotlin")],
|
||||
["hilt-gradle-plugin", version("hilt")],
|
||||
["compose-bom", version("composeBom")],
|
||||
["navigation-compose", version("navigation")],
|
||||
["room-runtime", version("room")],
|
||||
["datastore-preferences", version("dataStore")],
|
||||
["hilt-android", version("hilt")],
|
||||
["kotlin-stdlib", version("kotlin")],
|
||||
["junit4", version("junit")],
|
||||
]);
|
||||
@@ -71,5 +101,8 @@ if (failures.length > 0) {
|
||||
for (const failure of failures) console.error(`- ${failure}`);
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
console.log(`Dependency-license manifest passed (${manifest.components.length} reviewed components).`);
|
||||
console.log(
|
||||
`Dependency-license manifest passed (${manifest.components.length} components, ` +
|
||||
`${manifest.groupPolicies.length} group policies).`,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user