feat: split plugin builds

<!-- 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>
This commit is contained in:
Eli Bosley
2025-03-04 15:18:04 -05:00
committed by GitHub
parent 270072266a
commit d63e54bdbc
37 changed files with 6885 additions and 678 deletions

View File

@@ -0,0 +1,49 @@
import { getTxzName, LOCAL_BUILD_TAG, pluginNameWithExt } from "./consts";
// Define a common interface for URL parameters
interface UrlParams {
baseUrl: string;
tag?: string;
}
interface TxzUrlParams extends UrlParams {
pluginVersion: string;
}
/**
* Get the bucket path for the given tag
* ex. baseUrl = https://stable.dl.unraid.net/unraid-api
* ex. tag = PR123
* ex. returns = https://stable.dl.unraid.net/unraid-api/tag/PR123
*/
const getRootBucketPath = ({ baseUrl, tag }: UrlParams): URL => {
// Append tag to the baseUrl if tag is set, otherwise return the baseUrl
const url = new URL(baseUrl);
if (tag && tag !== LOCAL_BUILD_TAG) {
// Ensure the path ends with a trailing slash before adding the tag
url.pathname = url.pathname.replace(/\/?$/, "/") + "tag/" + tag;
}
return url;
};
/**
* Get the URL for the plugin file
* ex. returns = BASE_URL/TAG/dynamix.unraid.net.plg
*/
export const getPluginUrl = (params: UrlParams): string => {
const rootUrl = getRootBucketPath(params);
// Ensure the path ends with a slash and join with the plugin name
rootUrl.pathname = rootUrl.pathname.replace(/\/?$/, "/") + pluginNameWithExt;
return rootUrl.toString();
};
/**
* Get the URL for the main TXZ file
* ex. returns = BASE_URL/TAG/dynamix.unraid.net-4.1.3.txz
*/
export const getMainTxzUrl = (params: TxzUrlParams): string => {
const rootUrl = getRootBucketPath(params);
// Ensure the path ends with a slash and join with the txz name
rootUrl.pathname = rootUrl.pathname.replace(/\/?$/, "/") + getTxzName(params.pluginVersion);
return rootUrl.toString();
};

View File

@@ -0,0 +1,39 @@
import conventionalChangelog from "conventional-changelog";
import { PluginEnv } from "../cli/setup-plugin-environment";
export const getStagingChangelogFromGit = async ({
pluginVersion,
tag,
}: Pick<PluginEnv, "pluginVersion" | "tag">): Promise<string> => {
try {
const changelogStream = conventionalChangelog(
{
preset: "conventionalcommits",
},
{
version: pluginVersion,
},
tag
? {
from: "origin/main",
to: "HEAD",
}
: {},
undefined,
tag
? {
headerPartial: `## [${tag}](https://github.com/unraid/api/${tag})\n\n`,
}
: undefined
);
let changelog = "";
for await (const chunk of changelogStream) {
changelog += chunk;
}
// Encode HTML entities using the 'he' library
return changelog ?? "";
} catch (err) {
throw new Error(`Failed to get changelog from git: ${err}`);
}
};

View File

@@ -0,0 +1,18 @@
import { execSync } from "node:child_process";
import { deployDir } from "./paths";
import { mkdir } from "node:fs/promises";
import { startingDir } from "./consts";
import { join } from "node:path";
export const cleanupTxzFiles = async () => {
await mkdir(deployDir, { recursive: true });
const txzFiles = join(startingDir, deployDir, "*.txz");
await execSync(`rm -rf ${txzFiles}`);
};
export const cleanupPluginFiles = async () => {
await mkdir(deployDir, { recursive: true });
const pluginFiles = join(startingDir, deployDir, "*.plg");
await execSync(`rm -rf ${pluginFiles}`);
};

View File

@@ -0,0 +1,13 @@
export const pluginName = "dynamix.unraid.net" as const;
export const pluginNameWithExt = `${pluginName}.plg` as const;
export const getTxzName = (version?: string) =>
version ? `${pluginName}-${version}.txz` : `${pluginName}.txz`;
export const startingDir = process.cwd();
export const BASE_URLS = {
STABLE: "https://stable.dl.unraid.net/unraid-api",
PREVIEW: "https://preview.dl.unraid.net/unraid-api",
} as const;
export const LOCAL_BUILD_TAG = "LOCAL_PLUGIN_BUILD" as const;

View File

@@ -0,0 +1,43 @@
import { join } from "path";
import { getTxzName, pluginNameWithExt } from "./consts";
export interface PathConfig {
startingDir: string;
}
export interface TxzPathConfig extends PathConfig {
pluginVersion?: string;
}
export const deployDir = "deploy" as const;
/**
* Get the path to the root plugin directory
* @param startingDir - The starting directory
* @returns The path to the root plugin directory
*/
export function getRootPluginPath({ startingDir }: PathConfig): string {
return join(startingDir, "/plugins/", pluginNameWithExt);
}
/**
* Get the path to the deploy plugin directory
* @param startingDir - The starting directory
* @returns The path to the deploy plugin directory
*/
export function getDeployPluginPath({ startingDir }: PathConfig): string {
return join(startingDir, deployDir, pluginNameWithExt);
}
/**
* Get the path to the TXZ file
* @param startingDir - The starting directory
* @param pluginVersion - The plugin version
* @returns The path to the TXZ file
*/
export function getTxzPath({
startingDir,
pluginVersion,
}: TxzPathConfig): string {
return join(startingDir, deployDir, getTxzName(pluginVersion));
}