Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 76 additions & 29 deletions plugin/builder/build-txz.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,73 @@
import { join } from "path";
import { dirname, join } from "path";
import { tmpdir } from "os";
import { $, cd } from "zx";
import { existsSync } from "node:fs";
import { readdir, writeFile } from "node:fs/promises";
import { cp, mkdir, readdir, rm, writeFile } from "node:fs/promises";
import { getTxzName, pluginName, startingDir } from "./utils/consts";
import { ensureNodeJs } from "./utils/nodejs-helper";

import { setupTxzEnv, TxzEnv } from "./cli/setup-txz-environment";
import { cleanupTxzFiles } from "./utils/cleanup";
import { apiDir } from "./utils/paths";
import { getVendorBundleName, getVendorFullPath } from "./build-vendor-store";
import { getAssetUrl } from "./utils/bucket-urls";
import { validateStandaloneManifest, getStandaloneManifestPath } from "./utils/manifest-validator";

const mountedAssetsDir = join(startingDir, ".mounted-assets");
const mountedUuiDir = join(mountedAssetsDir, "uui");
const mountedStandaloneDir = join(mountedAssetsDir, "standalone");
const hostSourceDir = join(startingDir, "source");
const buildRootDir = join(tmpdir(), "connect-plugin-build");
const buildSourceDir = join(buildRootDir, "source");
const buildPluginDir = join(buildSourceDir, pluginName);
const buildWebcomponentsDir = join(
buildSourceDir,
"dynamix.unraid.net",
"usr",
"local",
"emhttp",
"plugins",
"dynamix.my.servers",
"unraid-components"
);
const buildApiDir = join(buildPluginDir, "usr", "local", "unraid-api");

const prepareBuildSourceDir = async () => {
await rm(buildRootDir, { force: true, recursive: true });
await mkdir(buildRootDir, { recursive: true });
await cp(hostSourceDir, buildSourceDir, { force: true, recursive: true });
};

const syncMountedAssetDir = async ({
sourceDir,
targetDir,
label,
}: {
sourceDir: string;
targetDir: string;
label: string;
}) => {
if (!existsSync(sourceDir)) {
return;
}

await rm(targetDir, { force: true, recursive: true });
await mkdir(dirname(targetDir), { recursive: true });
await cp(sourceDir, targetDir, { force: true, recursive: true });
console.log(`Synced ${label}: ${sourceDir} -> ${targetDir}`);
};

const syncMountedAssets = async () => {
await syncMountedAssetDir({
sourceDir: mountedUuiDir,
targetDir: join(buildWebcomponentsDir, "uui"),
label: "web components",
});
await syncMountedAssetDir({
sourceDir: mountedStandaloneDir,
targetDir: join(buildWebcomponentsDir, "standalone"),
label: "standalone assets",
});
};

// Check for manifest files in expected locations
const findManifestFiles = async (dir: string): Promise<string[]> => {
Expand Down Expand Up @@ -58,8 +114,7 @@ const storeVendorArchiveInfo = async (version: string, vendorUrl: string, vendor

// Create a config directory in the source tree
const configDir = join(
startingDir,
"source",
buildSourceDir,
"dynamix.unraid.net",
"usr",
"local",
Expand Down Expand Up @@ -106,27 +161,19 @@ const validateSourceDir = async (validatedEnv: TxzEnv) => {
if (!validatedEnv.ci) {
console.log("Validating TXZ source directory");
}
const sourceDir = join(startingDir, "source");
if (!existsSync(sourceDir)) {
throw new Error(`Source directory ${sourceDir} does not exist`);
if (!existsSync(hostSourceDir)) {
throw new Error(`Source directory ${hostSourceDir} does not exist`);
}
// Validate existence of webcomponent files:
// source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components
const webcomponentDir = join(
sourceDir,
"dynamix.unraid.net",
"usr",
"local",
"emhttp",
"plugins",
"dynamix.my.servers",
"unraid-components"
);
if (!existsSync(webcomponentDir)) {
throw new Error(`Webcomponent directory ${webcomponentDir} does not exist`);
await prepareBuildSourceDir();
await syncMountedAssets();

if (!existsSync(buildWebcomponentsDir)) {
throw new Error(
`Webcomponent directory ${buildWebcomponentsDir} does not exist`
);
}

const manifestFiles = await findManifestFiles(webcomponentDir);
const manifestFiles = await findManifestFiles(buildWebcomponentsDir);
const hasStandaloneManifest = manifestFiles.some(file =>
file === "standalone.manifest.json" || file === "standalone/standalone.manifest.json"
);
Expand All @@ -141,7 +188,7 @@ const validateSourceDir = async (validatedEnv: TxzEnv) => {
}

// Validate the manifest contents
const manifestPath = getStandaloneManifestPath(webcomponentDir);
const manifestPath = getStandaloneManifestPath(buildWebcomponentsDir);
if (manifestPath) {
const validation = await validateStandaloneManifest(manifestPath);

Expand All @@ -163,15 +210,15 @@ const validateSourceDir = async (validatedEnv: TxzEnv) => {
console.log("✅ Standalone manifest validation passed");
}

if (!existsSync(apiDir)) {
throw new Error(`API directory ${apiDir} does not exist`);
if (!existsSync(buildApiDir)) {
throw new Error(`API directory ${buildApiDir} does not exist`);
}
const packageJson = join(apiDir, "package.json");
const packageJson = join(buildApiDir, "package.json");
if (!existsSync(packageJson)) {
throw new Error(`API package.json file ${packageJson} does not exist`);
}
// Now CHMOD the api/dist directory files to allow execution
await $`chmod +x ${apiDir}/dist/*.js`;
await $`chmod +x ${buildApiDir}/dist/*.js`;
};

const buildTxz = async (validatedEnv: TxzEnv) => {
Expand Down Expand Up @@ -202,7 +249,7 @@ const buildTxz = async (validatedEnv: TxzEnv) => {

// Create package - must be run from within the pre-pack directory
// Use cd option to run command from prePackDir
await cd(join(startingDir, "source", pluginName));
await cd(buildPluginDir);
$.verbose = true;

// Create the package using the default package name
Expand Down
6 changes: 3 additions & 3 deletions plugin/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ services:
- ../.rclone-version:/app/.rclone-version
- ./source:/app/source
- ./scripts:/app/scripts
- ../unraid-ui/dist-wc:/app/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components/uui
- ../web/dist:/app/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components/standalone
- ../unraid-ui/dist-wc:/app/.mounted-assets/uui
- ../web/dist:/app/.mounted-assets/standalone
Comment on lines +14 to +15

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mount watched frontend assets back under /app/source

In the build:watch workflow, nodemon only watches source/**/* (see plugin/scripts/build-watcher.sh), but this change remounts ../unraid-ui/dist-wc and ../web/dist to /app/.mounted-assets/*, which is outside the watched tree. As a result, when those frontend artifacts are rebuilt on the host, the watcher no longer triggers a plugin rebuild, so local watch runs can keep producing packages with stale UI assets until a manual rebuild/restart.

Useful? React with 👍 / 👎.

- ../api/deploy/release/:/app/source/dynamix.unraid.net/usr/local/unraid-api # Use the release dir instead of pack to allow watcher to not try to build with node_modules
stdin_open: true # equivalent to -i
tty: true # equivalent to -t
environment:
- HOST_LAN_IP=${HOST_LAN_IP}
- CI=${CI:-false}
- TAG=${TAG}
- API_VERSION=${API_VERSION}
- API_VERSION=${API_VERSION}
12 changes: 10 additions & 2 deletions plugin/scripts/dc.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
#!/bin/bash

REPO_ROOT=$(cd .. && pwd)
REPO_GIT_DIR=${GIT_DIR:-../.git}

git_repo() {
git --git-dir="$REPO_GIT_DIR" --work-tree="$REPO_ROOT" "$@"
}

# Get host IP based on platform
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
Expand All @@ -17,10 +24,11 @@ fi

CI=${CI:-false}
TAG="LOCAL_PLUGIN_BUILD"
IS_TAGGED=$(git describe --tags --abbrev=0 --exact-match || echo '')
IS_TAGGED=$(git_repo describe --tags --abbrev=0 --exact-match 2>/dev/null || echo '')
PACKAGE_LOCK_VERSION=$(jq -r '.version' package.json)
GIT_SHA=$(git rev-parse --short HEAD)
GIT_SHA=$(git_repo rev-parse --short HEAD)
API_VERSION=$([[ -n "$IS_TAGGED" ]] && echo "$PACKAGE_LOCK_VERSION" || echo "${PACKAGE_LOCK_VERSION}+${GIT_SHA}")
export HOST_LAN_IP CI TAG API_VERSION

# Define container name for easier management
CONTAINER_NAME="plugin-builder"
Expand Down
Loading