Users can now switch between the enhanced color-coded log view and the original simple raw text view using a new toggle checkbox. The preference is saved to localStorage and persists across page reloads and different resources.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Add instantSaveSettings() method to save gzip, stripprefix, and
exclude_from_status checkboxes without triggering port validation modal.
These settings don't require domain/port validation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Updated color classes in NotifyDemo.php to use warning colors.
- Added new warning color variables in app.css.
- Changed warning icon colors in callout.blade.php.
- Updated loading spinner and hover states in global-search.blade.php.
- Refactored warning messages and styles in project application views.
- Adjusted log display colors in get-logs.blade.php.
- Updated private key status indicators in index.blade.php.
- Changed hover and text colors for documentation links in cloudflare-tunnel.blade.php.
- Refactored server creation messages in by-hetzner.blade.php.
- Updated proxy warning button colors in proxy.blade.php.
- Changed loading spinner colors in show.blade.php.
- Updated deployment status colors in deployments.blade.php and show.blade.php.
- Updated dashboard, destination, project, and server views to replace 'box' class with 'coolbox' for improved visual consistency.
- Modified links and buttons in shared variables and scheduled tasks views to utilize 'coolbox' class.
- Ensured all relevant components reflect the new styling approach, enhancing user experience and interface coherence.
Wrap ActivityMonitor components in wire:ignore to prevent parent component
re-renders from destroying the Livewire component and causing "Snapshot missing"
errors in production mode.
The issue occurred when toggling the "Backup includes all databases" checkbox
during database restore operations. The checkbox uses wire:model.live which
triggers immediate parent re-renders, destroying the nested ActivityMonitor
component in the slide-over.
Changes:
- Wrap ActivityMonitor in wire:ignore div in import.blade.php
- Apply same fix preventatively to heading.blade.php
wire:ignore prevents Livewire from re-rendering the DOM inside the wrapper,
while still allowing event listeners and Alpine.js functionality to work
correctly. The existing reset logic (slideOverClosed event) continues to
function properly.
Fixes#7335🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add two new application settings to control Docker build cache invalidation:
- inject_build_args_to_dockerfile (default: true) - Skip Dockerfile ARG injection
- include_source_commit_in_build (default: false) - Exclude SOURCE_COMMIT from build context
These toggles let users preserve Docker cache when SOURCE_COMMIT or custom ARGs change frequently. Development-only logging shows which ARGs are being injected for debugging.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes a critical bug in the environment variable autocomplete component
where arrow key navigation could cause divide-by-zero errors when the
suggestions array is empty.
Changes:
- Add early guard in handleKeydown to check for empty suggestions array
before performing modulo operations
- Remove unreachable "No suggestions" template that could never display
- Add validation to hide dropdown when user types third brace ({{{)
- Refactor add.blade.php to use @if directives instead of x-show for
better performance and cleaner code
The fix ensures arrow keys do nothing when suggestions are empty,
preventing JavaScript errors while maintaining all existing functionality
including the scoped empty state messages with helpful links.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add path attribute mutator to S3Storage model ensuring paths start with /
- Add updatedS3Path hook to normalize path and reset validation state on blur
- Add updatedS3StorageId hook to reset validation state when storage changes
- Add Enter key support to trigger file check from path input
- Use wire:model.live for S3 storage select, wire:model.blur for path input
- Improve shell escaping in restore job cleanup commands
- Fix isSafeTmpPath helper logic for directory validation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace hardcoded URL paths in getScopeUrl() with Laravel's route() helper
- Add scopeUrls property to EnvVarInput component with named routes
- Pass projectUuid and environmentUuid to enable context-specific environment links
- Environment scope link now navigates to the specific project/environment shared variables page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Load/Reload Compose File button at the top for easier access
- Add toggle to switch between raw and deployable Docker Compose views
- Improve code formatting and UI consistency
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Introduced tests for ContainerStatusAggregator to validate status aggregation logic across various container states.
- Implemented tests to ensure serverStatus accessor correctly checks server infrastructure health without being affected by container status.
- Updated ExcludeFromHealthCheckTest to verify excluded status handling in various components.
- Removed obsolete PushServerUpdateJobStatusAggregationTest as its functionality is covered elsewhere.
- Updated version number for sentinel to 0.0.17 in versions.json.
This commit adds:
- Comprehensive docker-compose examples for health check testing
- GitHub runner sources database migration
- UI fix for service heading view
Files Added:
- DOCKER_COMPOSE_EXAMPLES.md - Documentation of health check test cases
- docker-compose.*.yml - Test files for various health check scenarios:
- excluded.yml: Container with exclude_from_hc flag
- healthy.yml: All containers healthy
- unhealthy.yml: All containers unhealthy
- unknown.yml: Container without healthcheck
- mixed-healthy-unknown.yml: Mix of healthy and unknown
- mixed-unhealthy-unknown.yml: Mix of unhealthy and unknown
- database/migrations/2025_11_19_115504_create_github_runner_sources_table.php
Files Modified:
- resources/views/livewire/project/service/heading.blade.php
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When all containers are excluded from health checks, display their actual status
with :excluded suffix instead of misleading hardcoded statuses. This prevents
broken UI state with incorrect action buttons and provides clarity that monitoring
is disabled.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a new EnvVarInput component that provides autocomplete suggestions for shared environment variables from team, project, and environment scopes. Users can reference variables using {{ syntax.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Replace dynamic wire:key values that included the full command string
with stable, descriptive identifiers to prevent unnecessary re-renders
and potential issues with special characters.
Changes:
- Line 270: wire:key="preview-{{ $command }}" → "docker-compose-build-preview"
- Line 279: wire:key="start-preview-{{ $command }}" → "docker-compose-start-preview"
Benefits:
- Prevents element recreation on every keystroke
- Avoids issues with special characters in commands
- Better performance with long commands
- Follows Livewire best practices
The computed properties (dockerComposeBuildCommandPreview and
dockerComposeStartCommandPreview) continue to handle reactive updates
automatically, so preview content still updates as expected.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When using a custom Docker Compose build command, environment variables
were being lost because the --env-file flag was not included. This fix
automatically injects the --env-file flag to ensure build-time environment
variables are available during custom builds.
Changes:
- Auto-inject --env-file /artifacts/build-time.env after docker compose
- Respect user-provided --env-file flags (no duplication)
- Append build arguments when not using build secrets
- Update UI helper text to inform users about automatic env injection
- Add comprehensive unit tests (7 test cases, all passing)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace dynamic wire:key values that included the full command string
with stable, descriptive identifiers to prevent unnecessary re-renders
and potential issues with special characters.
Changes:
- Line 270: wire:key="preview-{{ $command }}" → "docker-compose-build-preview"
- Line 279: wire:key="start-preview-{{ $command }}" → "docker-compose-start-preview"
Benefits:
- Prevents element recreation on every keystroke
- Avoids issues with special characters in commands
- Better performance with long commands
- Follows Livewire best practices
The computed properties (dockerComposeBuildCommandPreview and
dockerComposeStartCommandPreview) continue to handle reactive updates
automatically, so preview content still updates as expected.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When using a custom Docker Compose build command, environment variables
were being lost because the --env-file flag was not included. This fix
automatically injects the --env-file flag to ensure build-time environment
variables are available during custom builds.
Changes:
- Auto-inject --env-file /artifacts/build-time.env after docker compose
- Respect user-provided --env-file flags (no duplication)
- Append build arguments when not using build secrets
- Update UI helper text to inform users about automatic env injection
- Add comprehensive unit tests (7 test cases, all passing)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major architectural improvements:
- Merged download and restore into single atomic operation
- Eliminated separate S3DownloadFinished event (redundant)
- Files now transfer directly: S3 → helper container → server → database container
- Removed download progress tracking in favor of unified restore progress
UI/UX improvements:
- Unified restore method selection with visual cards
- Consistent "File Information" display between local and S3 restore
- Single slide-over for all restore operations (removed separate S3 download monitor)
- Better visual feedback with loading states
Security enhancements:
- Added isSafeTmpPath() helper for path traversal protection
- URL decode validation to catch encoded attacks
- Canonical path resolution to prevent symlink attacks
- Comprehensive path validation in all cleanup events
Cleanup improvements:
- S3RestoreJobFinished now handles all cleanup (helper container + all temp files)
- RestoreJobFinished uses new isSafeTmpPath() validation
- CoolifyTask dispatches cleanup events even on job failure
- All cleanup uses non-throwing commands (2>/dev/null || true)
Other improvements:
- S3 storage policy authorization on Show component
- Storage Form properly syncs is_usable state after test
- Removed debug code and improved error handling
- Better command organization and documentation
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
The ActivityMonitor component was never rendered because:
1. x-show hides elements with CSS but doesn't affect DOM rendering
2. @if on ActivityMonitor evaluated to false on initial page load
3. When s3DownloadInProgress became true, x-show showed the div
4. But ActivityMonitor was never in the DOM to receive events
5. dispatch('activityMonitor') event was lost
Changed to use @if exclusively for all S3 download UI states:
- Button visibility controlled by @if instead of x-show
- Download progress section controlled by @if
- Downloaded file section controlled by @if
- Livewire automatically re-renders when state changes
- ActivityMonitor is properly added to DOM and receives events
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The first click did nothing because instant_remote_process() blocked the
Livewire response, preventing UI state updates. The button also remained
visible during download, allowing multiple clicks.
- Replace blocking instant_remote_process() with async command in queue
- Add container cleanup to command queue with error suppression
- Hide "Download & Prepare" button when s3DownloadInProgress is true
- Button now properly disappears when clicked, preventing double-clicks
- No more blocking operations in downloadFromS3() method
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The s3DownloadedFile was being set immediately when download started,
causing the "Restore" button to appear while still downloading and
the download message to not hide properly.
- Remove immediate setting of s3DownloadedFile in downloadFromS3()
- Set s3DownloadedFile only in handleS3DownloadFinished() event handler
- Add broadcastWith() to S3DownloadFinished to send downloadPath
- Store downloadPath as public property for broadcasting
- Now download message hides and restore button shows only when complete
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem:
- "Downloading from S3..." message stayed visible after download finished
- @if conditional only evaluates on server-side render, not reactive
- Event listener sets s3DownloadInProgress=false but view doesn't update
Solution:
- Wrap outer container with x-show="s3DownloadInProgress" for reactive hiding
- Keep @if for activity-monitor to control when it's rendered in DOM
- Message and success state now toggle reactively via Alpine.js entangle
- When download finishes, message hides immediately without page refresh
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause analysis:
- Changed from dispatch to property binding broke the activity monitor completely
- ActivityMonitor component expects activityMonitor event, not property binding
- Original approach was correct: use dispatch + event listeners
Solution:
- Revert to original dispatch('activityMonitor', $activity->id) calls
- Use @if conditionals to render only one monitor at a time (removes from DOM)
- Add unique wire:key to each monitor instance to prevent conflicts
- S3 download monitor: wire:key="s3-download-{{ $resource->uuid }}"
- Database restore monitor: wire:key="database-restore-{{ $resource->uuid }}"
This ensures:
- Activity monitors display correctly when processes start
- Only one monitor is rendered at a time (S3 download OR database restore)
- Each monitor has unique identity via wire:key
- Event listeners work as designed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add importRunning to x-data entangle bindings
- Change S3 download activity monitor from @if to x-show for real-time visibility
- Change database restore activity monitor from @if to x-show for real-time visibility
- Activity monitors now display reactively as state changes instead of only on page load
- Both monitors now visible immediately when processes start
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>