mirror of
https://github.com/unraid/api.git
synced 2025-12-31 13:39:52 -06:00
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced containerized plugin deployment support with updated Docker Compose configurations. - Added continuous build watch modes for API, web, and UI components for smoother development iterations. - Added a new job for API testing in the CI/CD workflow. - Added a new shell script to determine the local host's IP address for Docker configurations. - Introduced a new entry point and HTTP server setup in the plugin's Docker environment. - Added new scripts for building and watching plugin changes in real-time. - Added a new script for building the project in watch mode for the API and UI components. - **Improvements** - Streamlined the plugin installation process and refined release workflows for a more reliable user experience. - Enhanced overall CI/CD pipelines to ensure efficient, production-ready deployments. - Updated artifact upload paths and job definitions for clarity and efficiency. - Implemented new utility functions for better URL management and changelog generation. - Modified the `.dockerignore` file to ignore all contents within the `node_modules` directory. - Added new constants and functions for managing plugin paths and configurations. - Updated the build process in the Dockerfile to focus on release operations. - **Tests** - Expanded automated testing to validate environment setups and build stability, ensuring higher reliability during updates. - Introduced new test suites for validating plugin environment setups and configurations. - Added tests for validating environment variables and handling of manifest files. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Michael Datelle <mdatelle@icloud.com> Co-authored-by: mdatelle <mike@datelle.net> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Pujit Mehrotra <pujit@lime-technology.com>
123 lines
4.0 KiB
TypeScript
123 lines
4.0 KiB
TypeScript
import { z } from "zod";
|
|
import { access, constants, readFile } from "node:fs/promises";
|
|
import { Command } from "commander";
|
|
import { getStagingChangelogFromGit } from "../utils/changelog";
|
|
import { createHash } from "node:crypto";
|
|
import { getTxzPath } from "../utils/paths";
|
|
|
|
const safeParseEnvSchema = z.object({
|
|
ci: z.boolean().optional(),
|
|
baseUrl: z.string().url(),
|
|
tag: z.string().optional().default(''),
|
|
|
|
txzPath: z.string().refine((val) => val.endsWith(".txz"), {
|
|
message: "TXZ Path must end with .txz",
|
|
}),
|
|
pluginVersion: z.string().regex(/^\d{4}\.\d{2}\.\d{2}\.\d{4}$/, {
|
|
message: "Plugin version must be in the format YYYY.MM.DD.HHMM",
|
|
}),
|
|
releaseNotesPath: z.string().optional(),
|
|
});
|
|
|
|
const pluginEnvSchema = safeParseEnvSchema.extend({
|
|
releaseNotes: z.string().nonempty("Release notes are required"),
|
|
txzSha256: z.string().refine((val) => val.length === 64, {
|
|
message: "TXZ SHA256 must be 64 characters long",
|
|
}),
|
|
});
|
|
|
|
export type PluginEnv = z.infer<typeof pluginEnvSchema>;
|
|
|
|
export const validatePluginEnv = async (
|
|
envArgs: Record<string, any>
|
|
): Promise<PluginEnv> => {
|
|
const safeEnv = safeParseEnvSchema.parse(envArgs);
|
|
if (safeEnv.releaseNotesPath) {
|
|
await access(safeEnv.releaseNotesPath, constants.F_OK);
|
|
const releaseNotes = await readFile(safeEnv.releaseNotesPath, "utf8");
|
|
if (!releaseNotes || releaseNotes.length === 0) {
|
|
throw new Error(
|
|
`Release notes file is empty: ${safeEnv.releaseNotesPath}`
|
|
);
|
|
}
|
|
envArgs.releaseNotes = releaseNotes;
|
|
} else {
|
|
envArgs.releaseNotes =
|
|
process.env.TEST === "true"
|
|
? "FAST_TEST_CHANGELOG"
|
|
: await getStagingChangelogFromGit(safeEnv);
|
|
}
|
|
|
|
if (safeEnv.txzPath) {
|
|
await access(safeEnv.txzPath, constants.F_OK);
|
|
console.log("Reading txz file from:", safeEnv.txzPath);
|
|
const txzFile = await readFile(safeEnv.txzPath);
|
|
if (!txzFile || txzFile.length === 0) {
|
|
throw new Error(`TXZ Path is empty: ${safeEnv.txzPath}`);
|
|
}
|
|
envArgs.txzSha256 = getSha256(txzFile);
|
|
}
|
|
|
|
const validatedEnv = pluginEnvSchema.parse(envArgs);
|
|
|
|
if (validatedEnv.tag) {
|
|
console.warn("Tag is set, will generate a TAGGED build");
|
|
}
|
|
|
|
return validatedEnv;
|
|
};
|
|
|
|
export const getPluginVersion = () => {
|
|
const now = new Date();
|
|
|
|
const formatUtcComponent = (component: number) => String(component).padStart(2, '0');
|
|
|
|
const year = now.getUTCFullYear();
|
|
const month = formatUtcComponent(now.getUTCMonth() + 1);
|
|
const day = formatUtcComponent(now.getUTCDate());
|
|
const hour = formatUtcComponent(now.getUTCHours());
|
|
const minute = formatUtcComponent(now.getUTCMinutes());
|
|
|
|
const version = `${year}.${month}.${day}.${hour}${minute}`;
|
|
console.log("Plugin version:", version);
|
|
return version;
|
|
};
|
|
|
|
export const setupPluginEnv = async (argv: string[]): Promise<PluginEnv> => {
|
|
// CLI setup for plugin environment
|
|
const program = new Command();
|
|
|
|
program
|
|
.requiredOption(
|
|
"--base-url <url>",
|
|
"Base URL - will be used to determine the bucket, and combined with the tag (if set) to form the final URL",
|
|
process.env.CI === "true"
|
|
? "This is a CI build, please set the base URL manually"
|
|
: `http://${process.env.HOST_LAN_IP}:8080`
|
|
)
|
|
.option(
|
|
"--txz-path <path>",
|
|
"Path to built package, will be used to generate the SHA256 and renamed with the plugin version",
|
|
getTxzPath({ startingDir: process.cwd() })
|
|
)
|
|
.option(
|
|
"--plugin-version <version>",
|
|
"Plugin Version in the format YYYY.MM.DD.HHMM",
|
|
getPluginVersion()
|
|
)
|
|
.option("--tag <tag>", "Tag (used for PR and staging builds)", process.env.TAG)
|
|
.option("--release-notes-path <path>", "Path to release notes file")
|
|
.option("--ci", "CI mode", process.env.CI === "true")
|
|
.parse(argv);
|
|
|
|
const options = program.opts();
|
|
console.log("Options:", options);
|
|
const env = await validatePluginEnv(options);
|
|
console.log("Plugin environment setup successfully:", env);
|
|
return env;
|
|
};
|
|
|
|
function getSha256(txzBlob: Buffer): string {
|
|
return createHash("sha256").update(txzBlob).digest("hex");
|
|
}
|