mirror of
https://github.com/unraid/api.git
synced 2026-01-01 06:01:18 -06:00
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added detailed versioning for plugin packages incorporating architecture and build identifiers. - Simplified and improved install/uninstall scripts with backup and dynamic package detection. - Introduced comprehensive setup, verification, patching, and cleanup scripts for the Unraid API environment. - Enhanced service control with explicit start, stop, restart, and status commands. - Added robust dependency management scripts for restoring and archiving Node.js modules. - Implemented vendor archive metadata storage and dynamic handling during build and runtime. - Added new CLI options and environment schemas for consistent build configuration. - Introduced new shutdown scripts to gracefully stop flash-backup and unraid-api services. - Added utility scripts for API version detection and vendor archive configuration. - Added a new package description file detailing Unraid API features and homepage link. - **Bug Fixes** - Improved validation and error reporting for missing manifests, dependencies, and configuration files. - Enhanced fallback logic for locating and creating vendor archives. - Fixed iframe compatibility in UI by updating HTML and Firefox preference files. - **Chores** - Updated .gitignore with generated file patterns for Node.js binaries and archives. - Removed obsolete internal documentation and legacy cleanup scripts. - Refined Docker Compose and CI workflows to pass precise API versioning and manage build artifacts. - Centralized common environment validation and CLI option definitions across build tools. - Cleaned up plugin manifest by removing Node.js and PNPM-related entities and legacy logic. - Improved logging and error handling in build and installation scripts. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
134 lines
3.9 KiB
TypeScript
134 lines
3.9 KiB
TypeScript
import { readFile, writeFile, mkdir, rename } from "fs/promises";
|
|
import { $ } from "zx";
|
|
import { escape as escapeHtml } from "html-sloppy-escaper";
|
|
import { dirname, join } from "node:path";
|
|
import { getTxzName, pluginName, startingDir, defaultArch, defaultBuild } from "./utils/consts";
|
|
import { getAssetUrl, getPluginUrl } from "./utils/bucket-urls";
|
|
import { getMainTxzUrl } from "./utils/bucket-urls";
|
|
import {
|
|
deployDir,
|
|
getDeployPluginPath,
|
|
getRootPluginPath,
|
|
} from "./utils/paths";
|
|
import { PluginEnv, setupPluginEnv } from "./cli/setup-plugin-environment";
|
|
import { cleanupPluginFiles } from "./utils/cleanup";
|
|
import { bundleVendorStore, getVendorBundleName } from "./build-vendor-store";
|
|
|
|
/**
|
|
* Check if git is available
|
|
*/
|
|
const checkGit = async () => {
|
|
try {
|
|
await $`git log -1 --pretty=%B`;
|
|
} catch (err) {
|
|
console.error(`Error: git not available: ${err}`);
|
|
throw new Error(`Git not available: ${err}`);
|
|
}
|
|
};
|
|
|
|
const moveTxzFile = async ({txzPath, apiVersion}: Pick<PluginEnv, "txzPath" | "apiVersion">) => {
|
|
const txzName = getTxzName(apiVersion);
|
|
const targetPath = join(deployDir, txzName);
|
|
|
|
// Ensure the txz always has the full version name
|
|
if (txzPath !== targetPath) {
|
|
console.log(`Ensuring TXZ has correct name: ${txzPath} -> ${targetPath}`);
|
|
await rename(txzPath, targetPath);
|
|
} else {
|
|
console.log(`TXZ file already has correct name: ${txzPath}`);
|
|
}
|
|
};
|
|
|
|
function updateEntityValue(
|
|
xmlString: string,
|
|
entityName: string,
|
|
newValue: string
|
|
) {
|
|
console.log("Updating entity:", entityName, "with value:", newValue);
|
|
const regex = new RegExp(`<!ENTITY ${entityName} "[^"]*">`);
|
|
if (regex.test(xmlString)) {
|
|
return xmlString.replace(regex, `<!ENTITY ${entityName} "${newValue}">`);
|
|
}
|
|
throw new Error(`Entity ${entityName} not found in XML`);
|
|
}
|
|
|
|
const buildPlugin = async ({
|
|
pluginVersion,
|
|
baseUrl,
|
|
tag,
|
|
txzSha256,
|
|
releaseNotes,
|
|
apiVersion,
|
|
}: PluginEnv) => {
|
|
console.log(`API version: ${apiVersion}`);
|
|
|
|
// Update plg file
|
|
let plgContent = await readFile(getRootPluginPath({ startingDir }), "utf8");
|
|
|
|
// Update entity values
|
|
const entities: Record<string, string> = {
|
|
name: pluginName,
|
|
version: pluginVersion,
|
|
api_version: apiVersion,
|
|
arch: defaultArch,
|
|
build: defaultBuild,
|
|
plugin_url: getPluginUrl({ baseUrl, tag }),
|
|
txz_url: getMainTxzUrl({ baseUrl, apiVersion, tag }),
|
|
txz_sha256: txzSha256,
|
|
txz_name: getTxzName(apiVersion),
|
|
vendor_store_url: getAssetUrl({ baseUrl, tag }, getVendorBundleName(apiVersion)),
|
|
vendor_store_filename: getVendorBundleName(apiVersion),
|
|
...(tag ? { tag } : {}),
|
|
};
|
|
|
|
console.log("Entities:", entities);
|
|
// Iterate over entities and update them
|
|
Object.entries(entities).forEach(([key, value]) => {
|
|
if (!value) {
|
|
throw new Error(
|
|
`Entity ${key} not set in entities: ${JSON.stringify(entities)}`
|
|
);
|
|
}
|
|
plgContent = updateEntityValue(plgContent, key, value);
|
|
});
|
|
|
|
if (releaseNotes) {
|
|
// Update the CHANGES section with release notes
|
|
plgContent = plgContent.replace(
|
|
/<CHANGES>.*?<\/CHANGES>/s,
|
|
`<CHANGES>\n${escapeHtml(releaseNotes)}\n</CHANGES>`
|
|
);
|
|
}
|
|
|
|
await mkdir(dirname(getDeployPluginPath({ startingDir })), {
|
|
recursive: true,
|
|
});
|
|
console.log("Writing plg file to:", getDeployPluginPath({ startingDir }));
|
|
await writeFile(getDeployPluginPath({ startingDir }), plgContent);
|
|
};
|
|
|
|
/**
|
|
* Main build script
|
|
*/
|
|
|
|
const main = async () => {
|
|
try {
|
|
const validatedEnv = await setupPluginEnv(process.argv);
|
|
if (validatedEnv.tag === "LOCAL_PLUGIN_BUILD") {
|
|
console.log("Skipping git check for LOCAL_PLUGIN_BUILD");
|
|
} else {
|
|
await checkGit();
|
|
}
|
|
await cleanupPluginFiles();
|
|
|
|
await buildPlugin(validatedEnv);
|
|
await moveTxzFile(validatedEnv);
|
|
await bundleVendorStore(validatedEnv.apiVersion);
|
|
} catch (error) {
|
|
console.error(error);
|
|
process.exit(1);
|
|
}
|
|
};
|
|
|
|
await main();
|