Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
3902eb6
feat: add registry credential backend and runtime
mstrhakr Sep 13, 2026
f07c4d0
feat: add credential manager and stack selector UI
mstrhakr Sep 13, 2026
e080b63
fix: lock GitHub OAuth credential fields
mstrhakr Sep 13, 2026
d9351d8
fix: copy GitHub device code before authorization
mstrhakr Sep 13, 2026
5d60bf4
fix: support credential encryption without sodium
mstrhakr Sep 13, 2026
019aed6
feat: add registry provider presets and token helper links
mstrhakr Sep 14, 2026
15ff79f
fix: validate GitHub device session file write and chmod
mstrhakr Sep 14, 2026
d19a431
fix: catch credential vault errors in setStackSettings
mstrhakr Sep 14, 2026
96f70cf
fix: serialize vault read-modify-write transactions with flock
mstrhakr Sep 14, 2026
082748d
fix: sweep stale docker-config temp dirs and write vault/key atomically
mstrhakr Sep 14, 2026
126f2af
fix: reject client-supplied ids for new credentials and lock authMeth…
mstrhakr Sep 14, 2026
a352fab
fix: merge onto existing Docker config instead of replacing it wholesale
mstrhakr Sep 14, 2026
d39690c
perf: single-pass credential-to-stack mapping instead of per-credenti…
mstrhakr Sep 14, 2026
ba0081f
fix: sweep expired GitHub device-auth session files on start
mstrhakr Sep 14, 2026
acf332b
feat(credentials): add copy button and QR code for GitHub device sign-in
mstrhakr Sep 14, 2026
f59e39b
feat(credentials): log which registry credential is used for compose …
mstrhakr Sep 14, 2026
ad252a6
fix(credentials): surface credential name in operation output; add cr…
mstrhakr Sep 14, 2026
2e451fd
feat(credentials): add one-click renewal for OAuth GitHub credentials
mstrhakr Sep 14, 2026
888e024
fix(ui): wait for profile dialog to close
brandonocasey Sep 23, 2026
9ce7e23
Merge pull request #151 from mstrhakr/mstrhakr/issue148
mstrhakr Sep 24, 2026
dc34ca2
Merge pull request #154 from brandonocasey/fix-profile-dialog
mstrhakr Sep 24, 2026
6de5387
chore: sync pluginURL+README for dev branch [skip ci]
github-actions[bot] Sep 24, 2026
060efb8
fix(icons): support IPv6-only icon hosts
mstrhakr Sep 25, 2026
f9dddab
fix(autoupdate): limit concurrent image pulls
mstrhakr Sep 25, 2026
75f6213
chore: update changelog for v2026.09.25.0845 [skip ci]
github-actions[bot] Sep 25, 2026
efe3ab9
Release v2026.09.25.0845 [skip ci]
github-actions[bot] Sep 25, 2026
5cabd09
fix(auth): restrict GitHub credential renewal
mstrhakr Sep 25, 2026
5fa8131
fix(credentials): preserve active Docker configs
mstrhakr Sep 25, 2026
ac14033
fix(credentials): serialize assignment and deletion
mstrhakr Sep 25, 2026
1af75cf
chore: update changelog for v2026.09.25.1001 [skip ci]
github-actions[bot] Sep 25, 2026
3083775
Release v2026.09.25.1001 [skip ci]
github-actions[bot] Sep 25, 2026
64d7db5
fix(staticcheck): remove invalid credential lock template
mstrhakr Sep 25, 2026
546f75b
chore: update changelog for v2026.09.25.1058 [skip ci]
github-actions[bot] Sep 25, 2026
792170c
Release v2026.09.25.1058 [skip ci]
github-actions[bot] Sep 25, 2026
43a4dc7
fix(ui): wait for profile dialog before action dialog
mstrhakr Sep 29, 2026
7ff57a6
test(e2e): cover profile action dialog transition
mstrhakr Sep 29, 2026
12f72b4
fix(ui): wait until action dialog is fully closed
mstrhakr Sep 29, 2026
172ff16
Merge pull request #159 from mstrhakr/mstrhakr/issue156
mstrhakr Sep 30, 2026
28d76f6
chore: update changelog for v2026.09.30.0815 [skip ci]
github-actions[bot] Sep 30, 2026
8aebeda
Release v2026.09.30.0815 [skip ci]
github-actions[bot] Sep 30, 2026
8e6ba13
Fix picker-driven path validation updates
mstrhakr Sep 30, 2026
9ec146f
Merge pull request #160 from mstrhakr/mstrhakr/issue157
mstrhakr Sep 30, 2026
86735c6
fix(identity): make the identity chooser save the chosen project name
grantemsley Sep 30, 2026
2b45004
fix(compose): fail when the stack's env file is missing
grantemsley Sep 30, 2026
22092a3
fix(autoupdate): keep the last run time when saving auto-update settings
grantemsley Sep 30, 2026
f874197
fix(editor): read and save a relative env file path from the stack fo…
grantemsley Sep 30, 2026
f5d6e5c
fix(autoupdate): remove superseded images after updates
mstrhakr Oct 2, 2026
23320b8
Merge pull request #166 from mstrhakr/mstrhakr/issue158
mstrhakr Oct 2, 2026
dcba550
chore: update changelog for v2026.10.02.0849 [skip ci]
github-actions[bot] Oct 2, 2026
fa40359
Release v2026.10.02.0849 [skip ci]
github-actions[bot] Oct 2, 2026
a0725b4
Merge pull request #161 from grantemsley/fix/identity-chooser-endpoint
mstrhakr Oct 2, 2026
43bcb43
Merge pull request #162 from grantemsley/fix/compose-missing-env-file
mstrhakr Oct 2, 2026
c9bc16f
Merge pull request #163 from grantemsley/fix/autoupdate-keep-last-run
mstrhakr Oct 2, 2026
ee19bcc
chore: update changelog for v2026.10.02.0915 [skip ci]
github-actions[bot] Oct 2, 2026
7655e08
Merge pull request #164 from grantemsley/fix/editor-relative-envpath
mstrhakr Oct 2, 2026
10187ba
Release v2026.10.02.0915 [skip ci]
github-actions[bot] Oct 2, 2026
8aed62f
chore: update changelog for v2026.10.02.0916 [skip ci]
github-actions[bot] Oct 2, 2026
9f4a7f5
Release v2026.10.02.0916 [skip ci]
github-actions[bot] Oct 2, 2026
2b49ee6
Fix credential permissions and autoupdate propagation
mstrhakr Oct 2, 2026
07f6959
chore: update changelog for v2026.10.02.1007 [skip ci]
github-actions[bot] Oct 2, 2026
f9828b7
Release v2026.10.02.1007 [skip ci]
github-actions[bot] Oct 2, 2026
8e4430f
feat(compose): optionally create missing external networks before sta…
grantemsley Sep 30, 2026
2d0cfef
Merge pull request #167 from grantemsley/feat/create-missing-external…
mstrhakr Oct 4, 2026
fa9a0a6
Save rebuild-on-update when it is the only changed setting
Joly0 Oct 5, 2026
844a672
Merge branch 'dev' into fix-build-on-update-save
mstrhakr Oct 5, 2026
f496b8a
Merge pull request #168 from Joly0/fix-build-on-update-save
mstrhakr Oct 5, 2026
f2a2fbd
chore: update changelog for v2026.10.05.1006 [skip ci]
github-actions[bot] Oct 5, 2026
0e82a05
Release v2026.10.05.1006 [skip ci]
github-actions[bot] Oct 5, 2026
faa6fd0
fix(override): prune services the compose file no longer has
grantemsley Oct 4, 2026
6b12e70
fix(override): leave the override out with default file discovery too
grantemsley Oct 6, 2026
322c8dd
Merge pull request #169 from grantemsley/fix/prune-orphan-override-se…
mstrhakr Oct 6, 2026
4665a17
fix(icon): block IPv4-mapped private hosts
mstrhakr Oct 6, 2026
8457e4d
fix(credentials): honor selected registry auth
mstrhakr Oct 6, 2026
ae5a40e
fix(settings): validate credentials before saving
mstrhakr Oct 6, 2026
71cfefb
fix(credentials): reload list after deletion
mstrhakr Oct 6, 2026
236f744
chore: update changelog for v2026.10.06.1458 [skip ci]
github-actions[bot] Oct 6, 2026
3de5a21
Release v2026.10.06.1458 [skip ci]
github-actions[bot] Oct 6, 2026
0c4f96b
fix(credentials): log GitHub sign-in lifecycle and deletions
mstrhakr Oct 6, 2026
c50dafd
fix(loadlist): log HTTP status when a stack row request fails
mstrhakr Oct 6, 2026
3515bb3
chore: update changelog for v2026.10.06.1617 [skip ci]
github-actions[bot] Oct 6, 2026
0c1e61a
Release v2026.10.06.1617 [skip ci]
github-actions[bot] Oct 6, 2026
b4ad873
chore: add banner image to readme
mstrhakr Oct 7, 2026
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
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# Compose Manager Plus
<div align="center">
<img src="docs/images/banner.png" alt="Compose Manager Plus" width="900">

![GitHub Repo stars](https://img.shields.io/github/stars/mstrhakr/compose_plugin)
![GitHub branch check runs](https://img.shields.io/github/check-runs/mstrhakr/compose_plugin/main)
![GitHub Downloads (all assets, latest release)](https://img.shields.io/github/downloads/mstrhakr/compose_plugin/latest/total)
![GitHub Closed Issues](https://img.shields.io/github/issues-closed/mstrhakr/compose_plugin)
![GitHub Closed Pull Requests](https://img.shields.io/github/issues-pr-closed/mstrhakr/compose_plugin)

</div>

Compose Manager installs the Docker Compose CLI plugin on your unRAID server and provides a comprehensive web-based interface to create, run, update, back up, and restore Compose stacks directly from the unRAID dashboard. It includes per-stack autostart, scheduled automatic image updates, a rich context menu mirroring the native Docker tab experience, an integrated terminal for live command output, background command execution with notifications, optional UI integration to hide Compose-managed containers, and tooling for testing and CI.

## Screenshots
Expand Down
35 changes: 9 additions & 26 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.09.13">
<!ENTITY version "2026.10.06.1617">
<!ENTITY launch "Settings/&name;.settings">
<!ENTITY packageVER "&version;">
<!ENTITY packageMD5 "5dddb43f8ee98d2c67c953d3ebcbef19">
<!ENTITY pkgBUILD "2217">
<!ENTITY packageMD5 "dc3596bd965771122eebda37d716afe4">
<!ENTITY pkgBUILD "1617">
<!ENTITY packageName "&name;-&packageVER;-noarch-&pkgBUILD;">
<!ENTITY packagefile "&packageName;.txz">
<!ENTITY github "mstrhakr/compose_plugin">
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/main/&name;.plg">
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/dev/&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,28 +36,11 @@
>

<CHANGES>
###2026.09.13
- Features: add option to not close editor modal on outside click (#126)
- Bug Fixes: set remove-orphans checkbox checked state at render time instead of via setTimeout
- Bug Fixes: stop forced rebuilds on compose update
- Bug Fixes: adjust css so effective command is visible in all themes
- Bug Fixes: enhance logging and background processing in docker_started script
- Bug Fixes: enhance environment variable handling in build scripts and workflows
- Bug Fixes: dynamically resolve latest infozip package from Slackware repository
- Bug Fixes: enforce string type for path parameter in getComposeFilePath method
- Bug Fixes: support docker-compose stacks that resolve COMPOSE_FILE from .env in default discovery mode (#149)
- Bug Fixes: quiet routine dockerload reconnect warnings (#133)
- Chores: update changelog for v2026.09.13.1636 [skip ci]
- Chores: update changelog for v2026.09.07.2121 [skip ci]
- Chores: update changelog for v2026.09.07.2056 [skip ci]
- Chores: sync pluginURL+README for dev branch [skip ci]
- Chores: update changelog for v2026.09.06.2116 [skip ci]
- Chores: update changelog for v2026.09.06.1706 [skip ci]
- Chores: update changelog for v2026.09.05.2104 [skip ci]
- Fix #3: Add tests for indirect stack envpath metadata discovery
- Fix: Support relative envpath values in getExplicitEnvFilePath
- Fix: Pass stack directory to resolveProjectEnvFilePath for indirect stack envpath metadata resolution
- [View all changes](https://github.com/mstrhakr/compose_plugin/compare/v2026.09.03...v2026.09.13)
###2026.10.06.1617
- Bug Fixes (loadlist): log HTTP status when a stack row request fails
- Bug Fixes (credentials): log GitHub sign-in lifecycle and deletions
- [PR #155](https://github.com/mstrhakr/compose_plugin/pull/155)
- [beta release diff](https://github.com/mstrhakr/compose_plugin/compare/v2026.09.13...v2026.10.06.1617)
</CHANGES>

<!-- The 'pre-install' script. -->
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Access settings via **Settings → Compose** in the Unraid web UI.
| **Autostart: Wait for Docker** | No | Wait for Docker's autostart containers to finish before starting compose stacks. Useful when stacks depend on non-compose Docker containers. |
| **Autostart Docker Wait Timeout** | 120 | Seconds to wait for Docker autostart containers to stabilize (applies only when "Autostart: Wait for Docker" is enabled). |
| **Autostart Timeout** | 300 | Maximum time to wait for each stack to start during autostart (seconds). |
| **Create Missing External Networks** | No | Before a stack starts (Compose Up, Update, autostart), create any network it declares `external: true` that does not exist yet, as a plain bridge network. Docker Compose never creates external networks itself. Networks that need another driver or options (for example macvlan) must still be created by hand. |

### Display Options

Expand Down
Binary file added docs/images/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/banner.xcf
Binary file not shown.
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**
**Compose Manager Plus (Beta)**

A plugin for unRAID that installs Docker Compose and adds a management interface to the web UI.
60 changes: 56 additions & 4 deletions source/compose.manager/compose.manager.settings.page
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
<script src="<?php autov('/webGui/javascript/jquery.switchbutton.js'); ?>"></script>
<script src="<?php autov('/webGui/javascript/jquery.filetree.js'); ?>" charset="utf-8"></script>
<script src="<?php autov('/plugins/compose.manager/javascript/common.js'); ?>" type="text/javascript"></script>
<script src="<?php autov('/plugins/compose.manager/javascript/qrcode/qrcode.min.js'); ?>" type="text/javascript"></script>
<script src="<?php autov('/plugins/compose.manager/javascript/credentialManager.js'); ?>" type="text/javascript"></script>
<style>
/* Toggle switch overrides */
#compose-tab-settings .switch-button-background,
Expand Down Expand Up @@ -864,7 +866,7 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
}

var _ignorePatchUIChange = true; // prevent change handler from firing during initialization
$('#PROJECTS_FOLDER').on("input", null, null, checkProjectFolder);
$('#PROJECTS_FOLDER').on("input change", null, null, checkProjectFolder);

// Helper to initialize toggles as switchButton (only once)
function initSwitchToggles($scope) {
Expand Down Expand Up @@ -1189,6 +1191,7 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
AUTOUPDATE_ENABLED: $('#AUTOUPDATE_ENABLED').is(':checked') ? 'true' : 'false',
default_schedule: $('#default-schedule').val() || '',
default_time: $('#default-time').val() || '',
parallel_limit: $('#default-parallel-limit').val() || '',
rows: rows
};

Expand Down Expand Up @@ -1437,7 +1440,7 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
}, 'user', 'debug', 'settings');
}

if (settingsUnifiedState.activeTab === 'credits' || settingsUnifiedState.activeTab === 'log') {
if (settingsUnifiedState.activeTab === 'credits' || settingsUnifiedState.activeTab === 'log' || settingsUnifiedState.activeTab === 'credentials') {
$('#global-settings-actions').addClass('hidden');
} else {
$('#global-settings-actions').removeClass('hidden');
Expand Down Expand Up @@ -1595,7 +1598,7 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
updateUnifiedActionState();
});

$('#compose-tab-updates').on('input change', '#AUTOUPDATE_ENABLED, #default-schedule, #default-time, #updates-tbody input, #updates-tbody select', function() {
$('#compose-tab-updates').on('input change', '#AUTOUPDATE_ENABLED, #default-schedule, #default-time, #default-parallel-limit, #updates-tbody input, #updates-tbody select', function() {
settingsUnifiedState.updatesInitialized = true;
updateUnifiedActionState();
});
Expand Down Expand Up @@ -1649,6 +1652,9 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
} else {
stopLogRefresh();
}
if (tabId === 'credentials') {
ComposeCredentialManager.load();
}
// Always load backup archives when switching to backup tab
if (tabId === 'backup') {
loadBackupArchives();
Expand Down Expand Up @@ -1779,6 +1785,10 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
});
refreshCronStatus(finalizeInitialUpdates);
}

if (savedTab === 'credentials') {
ComposeCredentialManager.load();
}
});

function loadLogs() {
Expand Down Expand Up @@ -2369,6 +2379,10 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
}, function(cfg) {
// Response is already parsed as JSON by jQuery due to Content-Type header
if (typeof cfg !== 'object' || cfg === null) cfg = {};
var defaults = cfg.defaults || {};
$('#default-schedule').val(defaults.schedule || 'daily');
$('#default-time').val(defaults.time || '02:00');
$('#default-parallel-limit').val(defaults.parallel_limit || 4);
$('#updates-tbody').empty();
projects.forEach(function(p) {
var id = p.id;
Expand Down Expand Up @@ -2577,8 +2591,13 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
});
var defaults = {
schedule: $('#default-schedule').val(),
time: $('#default-time').val()
time: $('#default-time').val(),
parallel_limit: parseInt($('#default-parallel-limit').val(), 10)
};
if (isNaN(defaults.parallel_limit) || defaults.parallel_limit < 1 || defaults.parallel_limit > 16) {
defaults.parallel_limit = 4;
$('#default-parallel-limit').val(defaults.parallel_limit);
}
cfg['defaults'] = defaults;

var schedulerEnabled = $('#AUTOUPDATE_ENABLED').is(':checked');
Expand Down Expand Up @@ -2666,10 +2685,21 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
<div class="compose-tab active" data-tab="settings"><i class="fa fa-cog fa-fw"></i> _(Settings)_</div>
<div class="compose-tab" data-tab="updates"><i class="fa fa-refresh fa-fw"></i> _(Updates)_</div>
<div class="compose-tab" data-tab="backup"><i class="fa fa-archive fa-fw"></i> _(Backup / Restore)_</div>
<div class="compose-tab" data-tab="credentials"><i class="fa fa-key fa-fw"></i> _(Credentials)_</div>
<div class="compose-tab" data-tab="log"><i class="fa fa-file-text-o fa-fw"></i> _(Log)_</div>
<div class="compose-tab" data-tab="credits"><i class="fa fa-star fa-fw"></i> _(Credits)_</div>
</div>

<div id="compose-tab-credentials" class="compose-tab-content">
<div class="title"><span class="left"><i class="fa fa-key icon"></i>_(Registry Credentials)_</span></div>
<p>Store pull credentials locally and assign them to individual stacks.</p>
<button type="button" id="add-credential-button"><i class="fa fa-plus"></i> Add credential</button>
<table class="tablesorter" style="width:100%;margin-top:16px;">
<thead><tr><th>Name</th><th>Provider</th><th>Registry</th><th>Account</th><th>Used by</th><th>Actions</th></tr></thead>
<tbody id="credentials-tbody"><tr><td colspan="6" class="credential-empty">Open this tab to load credentials.</td></tr></tbody>
</table>
</div>

<!-- Settings Tab -->
<div id="compose-tab-settings" class="compose-tab-content active">
<form name="compose_manager_settings" method="POST" action="/update.php" target="progressFrame">
Expand Down Expand Up @@ -2775,6 +2805,18 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
</dd>
</dl>

<dl>
<dt style="cursor: help;" onclick="toggleHelp(this)">_(Create Missing External Networks)_:</dt>
<dd>
<input type="checkbox" name="CREATE_MISSING_EXTERNAL_NETWORKS" id="CREATE_MISSING_EXTERNAL_NETWORKS" class="compose-toggle" <?= ($cfg['CREATE_MISSING_EXTERNAL_NETWORKS'] ?? 'false') == 'true' ? 'checked' : '' ?>>
<blockquote class="inline_help">
Docker Compose never creates networks marked <code>external: true</code>, so a stack using one fails to start until the network exists.
When enabled, any missing external network is created as a plain bridge network right before a stack starts (Compose Up, Update and autostart).
Networks that need another driver or options (for example macvlan) must still be created by hand.
</blockquote>
</dd>
</dl>

<div class="title"><span class="left"><i class="fa fa-plus-square icon"></i>_(New Stack Configuration)_</span></div>
<p style="margin:0 0 15px 0;color:var(--dynamix-sb-title-text-color);font-size:0.95em;">Default settings applied automatically when creating new compose stacks.</p>

Expand Down Expand Up @@ -3050,6 +3092,16 @@ $acePath = file_exists('/usr/local/emhttp/plugins/dynamix/javascript/ace/ace.js'
</dd>
</dl>

<dl>
<dt style="cursor: help;" onclick="toggleHelp(this)">_(Image Pull Concurrency)_:</dt>
<dd>
<input type="number" id="default-parallel-limit" min="1" max="16" step="1" value="4" style="width:80px;"> _(pulls)_
<blockquote class="inline_help">
Limit concurrent image pulls during auto-updates. Lower values reduce registry throttling; the default is 4.
</blockquote>
</dd>
</dl>

<dl>
<dt>&nbsp;</dt>
<dd>
Expand Down
2 changes: 2 additions & 0 deletions source/compose.manager/default.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ REMOVE_ORPHANS_DEFAULT="false"
WAIT_FOR_HEALTHY_DEFAULT="false"
WAIT_FOR_HEALTHY_TIMEOUT_DEFAULT="300"
BUILD_ON_UPDATE_DEFAULT="false"
CREATE_MISSING_EXTERNAL_NETWORKS="false"
DISABLE_ACTION_WARNINGS="false"
NEW_STACK_USE_DEFAULT_COMPOSE_FILES="false"
NEW_STACK_OVERRIDE_MANAGEMENT_AUTOMATIC="true"
Expand All @@ -32,3 +33,4 @@ ONLY_EXPAND_RUNNING_STACKS="false"
COMPOSE_STATS_RATE_MODE="live"
COMPOSE_STATS_CUSTOM_INTERVAL_MS="1000"
DONT_CLOSE_EDITOR_MODAL_ON_OUTSIDE_CLICK="false"
GITHUB_OAUTH_CLIENT_ID="Ov23lip7HwI4IX2ueC97"
3 changes: 3 additions & 0 deletions source/compose.manager/event/docker_started
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ start_stack() {
if [ -n "$COMPOSE_SPEC_ENV_FILE_PATH" ]; then
cmd_args+=(-e "$COMPOSE_SPEC_ENV_FILE_PATH")
fi
if [ -n "$COMPOSE_SPEC_CREDENTIAL_ID" ]; then
cmd_args+=(--credential-id "$COMPOSE_SPEC_CREDENTIAL_ID")
fi
for profile in "${COMPOSE_SPEC_PROFILES[@]}"; do
cmd_args+=(-g "$profile")
done
Expand Down
46 changes: 44 additions & 2 deletions source/compose.manager/include/AutoUpdate.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
}
// Normalize keys: ensure top-level is array
if (!is_array($arr)) $arr = array();
if (!isset($arr['defaults']) || !is_array($arr['defaults'])) {
$arr['defaults'] = array();
}
$arr['defaults']['parallel_limit'] = compose_get_autoupdate_parallel_limit($arr);
// Filter top-level keys: only allow stack paths that pass isAllowedAutoUpdatePath
// Skip validation for 'defaults' key which stores default settings
$filtered = array();
Expand All @@ -59,7 +63,35 @@
break;
}
}
if (file_put_contents($autofile, json_encode($filtered, JSON_PRETTY_PRINT)) === false) {
// Same lock as AutoUpdateRunner, so a save and a scheduled run cannot overwrite each other.
$handle = fopen($autofile, 'c+');
if (!$handle || !flock($handle, LOCK_EX)) {
if ($handle) fclose($handle);
http_response_code(500);
echo json_encode(array('error' => 'Failed to lock config file'));
break;
}
// The settings page only sends the fields it shows, so keep the stored ones it does not
// (such as last_run, without which the runner updates the stack again).
$existing = json_decode((string) stream_get_contents($handle), true);
if (!is_array($existing)) $existing = array();
foreach ($filtered as $key => $config) {
if (is_array($config) && isset($existing[$key]) && is_array($existing[$key])) {
$filtered[$key] = array_merge($existing[$key], $config);
}
}
// Encode before truncating, so a failed encode leaves the file as it was.
$json = json_encode($filtered, JSON_PRETTY_PRINT);
$written = false;
if ($json !== false) {
ftruncate($handle, 0);
rewind($handle);
$written = fwrite($handle, $json);
fflush($handle);
}
flock($handle, LOCK_UN);
fclose($handle);
if ($written === false) {
http_response_code(500);
echo json_encode(array('error' => 'Failed to write config file'));
break;
Expand Down Expand Up @@ -143,16 +175,23 @@
$composeFileList = $stackInfo->buildComposeFileList();
$envFilePath = $args['envFilePath'] ?? null;
$projectDirectory = $args['projectDirectory'];
$credentialId = $stackInfo->getCredentialId();
} else {
$composeFileList = $composeFile;
$envFilePath = null;
$projectDirectory = $path;
$credentialIdFile = rtrim($path, '/') . '/credential_id';
$credentialId = is_file($credentialIdFile)
? trim((string) file_get_contents($credentialIdFile))
: null;
}

// Allow overriding the shell command via environment for tests; default to sh
$shCmd = getenv('COMPOSE_MANAGER_SH') ? getenv('COMPOSE_MANAGER_SH') : 'sh';
$autoUpdateConfig = is_file($autofile) ? json_decode((string) file_get_contents($autofile), true) : array();
$parallelLimit = compose_get_autoupdate_parallel_limit(is_array($autoUpdateConfig) ? $autoUpdateConfig : array());

$envPrefix = '';
$envPrefix = 'COMPOSE_PARALLEL_LIMIT=' . escapeshellarg((string) $parallelLimit) . ' ';
Comment thread
mstrhakr marked this conversation as resolved.
if ($composeFileList !== '') {
$envPrefix .= 'COMPOSE_FILE_LIST=' . escapeshellarg($composeFileList) . ' ';
}
Expand All @@ -162,6 +201,9 @@
if ($composeFileList === '' && $projectDirectory !== '') {
$envPrefix .= 'COMPOSE_PROJECT_DIR=' . escapeshellarg($projectDirectory) . ' ';
}
if ($credentialId !== null && $credentialId !== '') {
$envPrefix .= 'COMPOSE_CREDENTIAL_ID=' . escapeshellarg($credentialId) . ' ';
}

$cmd = $envPrefix . $shCmd . ' ' . escapeshellarg($script) . " " . escapeshellarg($projectName) . " 2>&1";
exec($cmd, $output, $rc);
Expand Down
Loading
Loading