Skip to content
Merged
102 changes: 95 additions & 7 deletions compose.manager.plg
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
<!DOCTYPE PLUGIN [
<!ENTITY name "compose.manager">
<!ENTITY author "mstrhakr">
<!ENTITY version "2026.08.31.1552">
<!ENTITY version "2026.08.31">
<!ENTITY launch "Settings/&name;.settings">
<!ENTITY packageVER "&version;">
<!ENTITY packageMD5 "19f6203e4025b11daeb9d8951526ded2">
<!ENTITY pkgBUILD "1552">
<!ENTITY packageMD5 "c0154198766fba55fb422b88db6382f6">
<!ENTITY pkgBUILD "2148">
<!ENTITY packageName "&name;-&packageVER;-noarch-&pkgBUILD;">
<!ENTITY packagefile "&packageName;.txz">
<!ENTITY github "mstrhakr/compose_plugin">
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/dev/&name;.plg">
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/main/&name;.plg">
<!ENTITY packageURL "https://github.com/&github;/releases/download/v&version;/&packagefile;">
<!ENTITY pluginsPATH "/boot/config/plugins">
<!ENTITY pluginLOC "&pluginsPATH;/&name;">
Expand All @@ -36,10 +36,98 @@
>

<CHANGES>
###2026.08.31.1552
###2026.08.31
- Features (icon): cache and normalize icons, seed Docker Manager cache (#141)
- Features (icon): add debug logging for icon cache operations (#141)
- Features (icon): bundle resvg for SVG → PNG conversion, add GD to CI (#141)
- Features (tests): add GD extension to PHP setup in workflow
- Features (icon): seed Docker Manager cache during container icon resolution (#141)
- Features (icon): add fetch, convert, and DM-seed helpers (#141)
- Features (icon): add plugin icon cache path helper (#141)
- Features (settings): enhance effective command display with improved styling and structure
- Features (add-stack): inline validation, slug preview, gated Create button
- Features (add-stack): Automatic/Manual radio for Override File Management
- Features (add-stack): shared discovery-mode badge + toggle
- Features (add-stack): Compose Source radio group via shared helper
- Features (editor): promote Compose Sources &amp; Files to its own tab
- Features (editor): inline settings validation blocks Apply until resolved
- Features (editor): live effective docker compose command preview
- Features (editor): status badge + explicit toggle for compose file discovery
- Features (editor): merge External Compose Path &amp; File into Compose Source radio
- Features: enhance error handling and metadata retrieval in compose loading process
- Features: implement client-side control for compose_info publisher and enhance WebSocket management for hidden tabs
- Features: add additional ignored error messages for PHPStan analysis
- Features: implement plugin-local update check for Compose Manager to enhance image update accuracy
- Features (tests): enhance foreground operation tests for Compose Manager with modal interactions and image validation
- Features (tests): add UI operation flow tests for Compose Manager and extract shared helpers
- Features (notification): add composeSendNotification function for stack delete notifications
- Features (e2e): add Playwright end-to-end tests for Compose Manager
- Features (cache): add function to purge deleted stack caches and update UI accordingly
- Features (settings): enhance auto-check updates functionality with mode selection and custom intervals
- Features (settings-page): add functions to count changed settings and update UI accordingly
- Features (settings-page): enhance global actions and settings form handling
- Features (settings-page): enhance defaults with auto/manual verbage
- Features (settings-page): enhance settings tab with descriptive help text and rephrase section titles
- Features (settings-page): reorganize and enhance update checking section
- Features (column-layout): implement shared column layout and visibility helpers
- Bug Fixes (codeql): add sanitization for user-entered icon values in live preview
- [PR #139](https://github.com/mstrhakr/compose_plugin/pull/139)
- [beta release diff](https://github.com/mstrhakr/compose_plugin/compare/v2026.07.02...v2026.08.31.1552)
- Bug Fixes (codeql): avoid html sink in labels renderer
- Bug Fixes (build): add environment variables for RESVG version and SHA256 in Docker build
- Bug Fixes (icon): simplify PNG detection logic and enhance test environment setup for Docker Manager RAM icons
- Bug Fixes (icon): enhance PNG handling in icon processing functions and simplify tests by removing GD dependency checks
- Bug Fixes (stack): update orphan removal logic to handle stack mismatches
- Bug Fixes (icon): fix alpha transparency in GD PNG conversion; add global Clear Icon Cache button (#141)
- Bug Fixes (icon): serve icons through local cache proxy instead of remote URLs (#141)
- Bug Fixes (icon): clear plugin icon cache on clearIconCache action (#141)
- Bug Fixes (icon): guard fallback loop for invalid icons (#141)
- Bug Fixes: trim Bearer auth challenge keys and limit explode
- Bug Fixes: allow symlink deletion in composeDeleteStackFolder
- Bug Fixes: enhance WebSocket subscriber management to prevent stale connections and messages
- Bug Fixes: add composeResolveDockerHostIp function to retrieve Docker host IP across Unraid WebUI versions fixes #127
- Bug Fixes (tests): use PATH_SEPARATOR for COMPOSE_FILE in env file to support Windows development
- Bug Fixes (icons): resolve native Docker tab/Dashboard icons for local paths (#129)
- Bug Fixes: add missing Playwright dependencies installation step in E2E test setup
- Bug Fixes: ensure function definitions are wrapped in function_exists checks to prevent redeclaration errors
- Bug Fixes: remove HTML coverage report generation from PHPUnit configuration
- Bug Fixes: enhance image digest retrieval and add running container check in auto-update script to match manual update path Fixes #132
- Bug Fixes: ensure new stacks create app-managed project override templates and update tests accordingly Fixes #134
- Bug Fixes: ensure proper exit code propagation for compose commands and record operation exit status
- Bug Fixes (nchan): optimize docker stats publishing and address shared-memory exhaustion
- Bug Fixes: enhance stack update handling with improved reload queue management
- Bug Fixes (tests): ensure tests remove containers each time
- Bug Fixes (settings): remove unused referrer handling script for cleaner settings page
- Bug Fixes (settings): add debug logging functions for settings changes and state tracking
- Bug Fixes (stack): simplify removal logic in composeDeleteStackFolder function
- Bug Fixes (ui): make stack deletion dynamic without full reload
- Bug Fixes (stack): guard and harden deleteStack folder removal
- Bug Fixes (menu): centralize header menu visibility condition
- Bug Fixes (compose-pull): refactor ComposePullConfirmed function to use options object for parameters
- Bug Fixes (sync-plugin-url): correct escaping in sed command for pluginURL update
- Refactoring (build): reorganize cache path definitions in build scripts
- Refactoring (Exec): remove null coalescing for project name and directory
- Refactoring (pkg_build): enhance checksum handling in download function
- Refactoring (add-stack): section-based layout with editor vocabulary
- Refactoring (editor): split per-stack Advanced section into three subsections
- Refactoring (settings): move REMOVE_ORPHANS_DEFAULT to global settings page
- Refactoring: remove basic output style handling
- Refactoring (settings-page): reorganize settings and update help funcs
- Documentation: clarify registry authentication section in UpdateCheck
- Tests (icon): fix hardcoded cache path expectation to use COMPOSE_ICON_CACHE_DIR constant
- Tests (icon): add IconCacheTest and bootstrap COMPOSE_ICON_CACHE_DIR temp path (#141)
- Chores: update changelog for v2026.08.31.1552 [skip ci]
- Chores: update changelog for v2026.08.31.1513 [skip ci]
- Chores: update changelog for v2026.08.31.1142 [skip ci]
- Chores: update changelog for v2026.08.30.1848 [skip ci]
- Chores: update changelog for v2026.08.30.1823 [skip ci]
- Chores: update changelog for v2026.08.30.1818 [skip ci]
- Chores: update changelog for v2026.08.27.2133 [skip ci]
- Chores: update changelog for v2026.08.27.2113 [skip ci]
- Chores: update COMPOSE_VERSION to 5.5.0
- Chores: update changelog for v2026.08.23.2252 [skip ci]
- Chores: sync pluginURL+README for dev branch [skip ci]
- Chores (columns): cleanup advanced/basic view mode references and legacy logic
- Refactor IconTest and remove redundant tests; add ComposeListEndpointTest
- [View all changes](https://github.com/mstrhakr/compose_plugin/compare/v2026.07.02...v2026.08.31)
</CHANGES>

<!-- The 'pre-install' script. -->
Expand Down
44 changes: 27 additions & 17 deletions source/compose.manager/IconCache.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php
/**
* Serve a plugin-cached icon PNG to the browser.
* ?src=<url-encoded-source> — fetch/hit cache then output bytes.
* Falls back to 302 redirect for http(s) sources not yet cached.
* ?src=<url-encoded-source> — always answered from the plugin icon cache.
* ?ct=<container> — also repair that container's Docker Manager icon.
* ?refresh=1 — force a re-fetch before serving.
*/
require_once __DIR__ . '/include/Defines.php';
require_once __DIR__ . '/include/Util.php';
Expand All @@ -13,24 +14,33 @@
exit;
}

$cached = compose_fetch_icon_to_cache($src);
$containerName = isset($_GET['ct']) ? trim((string) $_GET['ct']) : '';
if ($containerName !== '' && preg_match('#^[a-zA-Z0-9][a-zA-Z0-9._-]*$#', $containerName) !== 1) {
$containerName = '';
}

if ($cached !== '' && file_exists($cached)) {
header('Content-Type: image/png');
header('Cache-Control: public, max-age=86400');
header('X-Content-Type-Options: nosniff');
readfile($cached);
exit;
$forceRefresh = isset($_GET['refresh']) && (string) $_GET['refresh'] === '1';
$cachePath = compose_get_icon_cache_path($src);

// Populate only when the cache is missing, stale, or holds non-PNG bytes; a
// failed refresh keeps serving the last known-good PNG.
if ($forceRefresh || compose_icon_cache_is_stale($src, $cachePath)) {
if (compose_fetch_icon_to_cache($src, true) === '' && compose_file_is_png($cachePath)) {
composeLogger('Icon refresh failed; serving cached copy', ['source' => $src], 'system', 'debug', 'icon-cache');
}
}

// Cache miss (conversion unsupported or fetch failed) — redirect to original
// Only follow through for http(s); anything else is a 404
if (
filter_var($src, FILTER_VALIDATE_URL) !== false
&& (strpos($src, 'https://') === 0 || strpos($src, 'http://') === 0)
) {
header('Location: ' . $src, true, 302);
if (!compose_file_is_png($cachePath)) {
header('Location: /plugins/compose.manager/images/question.png', true, 302);
exit;
}

http_response_code(404);
if ($containerName !== '') {
compose_seed_docker_manager_icon($cachePath, $containerName);
}

header('Content-Type: image/png');
header('Cache-Control: public, max-age=3600');
header('X-Content-Type-Options: nosniff');
header('Content-Length: ' . (string) filesize($cachePath));
readfile($cachePath);
2 changes: 1 addition & 1 deletion source/compose.manager/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
**Compose Manager Plus (Beta)**
**Compose Manager Plus**

A plugin for unRAID that installs Docker Compose and adds a management interface to the web UI.
2 changes: 1 addition & 1 deletion source/compose.manager/compose.manager.dashboard.page
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ $script .= <<<'EOT'
var stateColor = isRunning ? 'green-text' : 'red-text';
var stateText = isRunning ? 'started' : 'stopped';
var ctElId = 'dash-ct-' + ctIdShort;
var imgSrc = composeIconSrc(ctIcon);
var imgSrc = composeIconSrc(ctIcon, ctName);
var shell = ctShell;
var shortId = ctIdShort;
var webui = resolveContainerWebUI(ctWebUI);
Expand Down
4 changes: 4 additions & 0 deletions source/compose.manager/include/Defines.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ function locate_compose_root($name)
defined('COMPOSE_ICON_NORMALIZE_DIR') || define('COMPOSE_ICON_NORMALIZE_DIR', '/var/tmp/compose-icon-normalize');
defined('COMPOSE_ICON_CACHE_DIR') || define('COMPOSE_ICON_CACHE_DIR', '/boot/config/plugins/compose.manager/icon-cache');
defined('COMPOSE_RESVG_BIN') || define('COMPOSE_RESVG_BIN', '/usr/local/emhttp/plugins/compose.manager/bin/resvg');
defined('COMPOSE_DM_ICON_RAM_DIR') || define('COMPOSE_DM_ICON_RAM_DIR', '/usr/local/emhttp/state/plugins/dynamix.docker.manager/images');
defined('COMPOSE_DM_ICON_PERSIST_DIR') || define('COMPOSE_DM_ICON_PERSIST_DIR', '/var/lib/docker/unraid/images');
defined('COMPOSE_DM_WEBUI_INFO_FILE') || define('COMPOSE_DM_WEBUI_INFO_FILE', '/usr/local/emhttp/state/plugins/dynamix.docker.manager/docker.json');
defined('COMPOSE_DOCKER_LABEL_ICON') || define('COMPOSE_DOCKER_LABEL_ICON', 'net.unraid.docker.icon');

/**
* Reserved filename at the compose root level used by the plugin installer
Expand Down
7 changes: 7 additions & 0 deletions source/compose.manager/include/Exec.php
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,13 @@ function composeResolveContainerIcon(string $containerName, string $service, arr
$rawContainer['Icon'] = $labels[$docker_label_icon] ?? '';
$rawContainer['Shell'] = $labels[$docker_label_shell] ?? '/bin/bash';

if ($ctName !== '' && trim((string) $rawContainer['Icon']) !== '') {
compose_seed_docker_manager_icon(
compose_fetch_icon_to_cache((string) $rawContainer['Icon']),
$ctName
);
}

// Resolve WebUI URL server-side (matching Unraid's DockerClient logic)
$networkMode = $inspect['HostConfig']['NetworkMode'] ?? 'bridge';
if (strpos($networkMode, ':') !== false) {
Expand Down
Loading
Loading