Merge pull request #6598 from ossamalafhel/feature/watch-paths-for-services
feat: 👀 Show Watch Paths field for Docker Compose applicationsFeature/watch paths for services
This commit is contained in:
commit
38e5e85344
3 changed files with 207 additions and 1 deletions
33
CHANGELOG.md
33
CHANGELOG.md
|
|
@ -4,6 +4,33 @@ # Changelog
|
|||
|
||||
## [unreleased]
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(docker)* Adjust openssh-client installation in Dockerfile to avoid version bug
|
||||
- *(docker)* Streamline openssh-client installation in Dockerfile
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- Update changelog
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(versions)* Increment coolify version numbers to 4.0.0-beta.431 and 4.0.0-beta.432 in configuration files
|
||||
|
||||
## [4.0.0-beta.430] - 2025-09-24
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(PreviewCompose)* Adds port to preview urls
|
||||
- *(deployment-job)* Enhance build time variable analysis
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- Update changelog
|
||||
- Update changelog
|
||||
|
||||
## [4.0.0-beta.429] - 2025-09-23
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(environment)* Replace is_buildtime_only with is_runtime and is_buildtime flags for environment variables, updating related logic and views
|
||||
|
|
@ -23,6 +50,8 @@ ### 🚀 Features
|
|||
- *(cloud-check)* Enhance CloudCheckSubscription command with fix options
|
||||
- *(stripe)* Enhance subscription handling and verification process
|
||||
- *(private-key-refresh)* Add refresh dispatch on private key update and connection check
|
||||
- *(comments)* Add automated comments for labeled pull requests to guide documentation updates
|
||||
- *(comments)* Ping PR author
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
|
|
@ -42,6 +71,8 @@ ### 🐛 Bug Fixes
|
|||
- *(environment-variables)* Correct method call syntax in analyzeBuildVariable function
|
||||
- *(clears-global-search-cache)* Refine team retrieval logic in getTeamIdForCache method
|
||||
- *(subscription-job)* Enhance retry logic for VerifyStripeSubscriptionStatusJob
|
||||
- *(environment-variable)* Update checkbox visibility and helper text for build and runtime options
|
||||
- *(deployment-job)* Escape single quotes in build arguments for Docker Compose command
|
||||
|
||||
### 🚜 Refactor
|
||||
|
||||
|
|
@ -75,6 +106,8 @@ ### 🎨 Styling
|
|||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- Change order of runtime and buildtime
|
||||
- *(docker-compose)* Update soketi image version to 1.0.10 in production and Windows configurations
|
||||
- *(versions)* Update coolify version numbers to 4.0.0-beta.430 and 4.0.0-beta.431 in configuration files
|
||||
|
||||
## [4.0.0-beta.428] - 2025-09-15
|
||||
|
||||
|
|
|
|||
|
|
@ -268,6 +268,14 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
|
|||
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} up -d</span>"
|
||||
label="Custom Start Command" />
|
||||
</div>
|
||||
@if ($this->application->is_github_based())
|
||||
<div class="pt-4">
|
||||
<x-forms.textarea
|
||||
helper="Gitignore-style rules to filter Git based webhook deployments."
|
||||
placeholder="services/api/**" id="application.watch_paths"
|
||||
label="Watch Paths" x-bind:disabled="shouldDisable()" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
|
|
@ -299,7 +307,7 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
|
|||
@endif
|
||||
|
||||
</div>
|
||||
@if ($this->application->is_github_based() && !$this->application->is_public_repository())
|
||||
@if ($this->application->is_github_based())
|
||||
<div class="pb-4">
|
||||
<x-forms.textarea
|
||||
helper="Gitignore-style rules to filter Git based webhook deployments."
|
||||
|
|
|
|||
165
tests/Unit/ApplicationWatchPathsTest.php
Normal file
165
tests/Unit/ApplicationWatchPathsTest.php
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\Application;
|
||||
use Illuminate\Support\Collection;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ApplicationWatchPathsTest extends TestCase
|
||||
{
|
||||
public function test_is_watch_paths_triggered_returns_false_when_watch_paths_is_null()
|
||||
{
|
||||
$application = new Application();
|
||||
$application->watch_paths = null;
|
||||
|
||||
$modified_files = collect(['docker-compose.yml', 'README.md']);
|
||||
|
||||
$this->assertFalse($application->isWatchPathsTriggered($modified_files));
|
||||
}
|
||||
|
||||
public function test_is_watch_paths_triggered_with_exact_match()
|
||||
{
|
||||
$application = new Application();
|
||||
$application->watch_paths = "docker-compose.yml\nDockerfile";
|
||||
|
||||
// Exact match should return true
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['docker-compose.yml'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['Dockerfile'])));
|
||||
|
||||
// Non-matching file should return false
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['README.md'])));
|
||||
}
|
||||
|
||||
public function test_is_watch_paths_triggered_with_wildcard_patterns()
|
||||
{
|
||||
$application = new Application();
|
||||
$application->watch_paths = "*.yml\nsrc/**/*.php\nconfig/*";
|
||||
|
||||
// Wildcard matches
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['docker-compose.yml'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['production.yml'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['src/Controllers/UserController.php'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['src/Models/User.php'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['config/app.php'])));
|
||||
|
||||
// Non-matching files
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['README.md'])));
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['src/index.js'])));
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['configurations/deep/file.php'])));
|
||||
}
|
||||
|
||||
public function test_is_watch_paths_triggered_with_multiple_files()
|
||||
{
|
||||
$application = new Application();
|
||||
$application->watch_paths = "docker-compose.yml\n*.env";
|
||||
|
||||
// At least one file matches
|
||||
$modified_files = collect(['README.md', 'docker-compose.yml', 'package.json']);
|
||||
$this->assertTrue($application->isWatchPathsTriggered($modified_files));
|
||||
|
||||
// No files match
|
||||
$modified_files = collect(['README.md', 'package.json', 'src/index.js']);
|
||||
$this->assertFalse($application->isWatchPathsTriggered($modified_files));
|
||||
}
|
||||
|
||||
public function test_is_watch_paths_triggered_with_complex_patterns()
|
||||
{
|
||||
$application = new Application();
|
||||
// fnmatch doesn't support {a,b} syntax, so we need to use separate patterns
|
||||
$application->watch_paths = "**/*.js\n**/*.jsx\n**/*.ts\n**/*.tsx";
|
||||
|
||||
// JavaScript/TypeScript files should match
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['src/index.js'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['components/Button.jsx'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['types/user.ts'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['pages/Home.tsx'])));
|
||||
|
||||
// Deeply nested files should match
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['src/components/ui/Button.tsx'])));
|
||||
|
||||
// Non-matching files
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['README.md'])));
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['package.json'])));
|
||||
}
|
||||
|
||||
public function test_is_watch_paths_triggered_with_question_mark_pattern()
|
||||
{
|
||||
$application = new Application();
|
||||
$application->watch_paths = "test?.txt\nfile-?.yml";
|
||||
|
||||
// Single character wildcard matches
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['test1.txt'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['testA.txt'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['file-1.yml'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['file-B.yml'])));
|
||||
|
||||
// Non-matching files
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['test.txt'])));
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['test12.txt'])));
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['file.yml'])));
|
||||
}
|
||||
|
||||
public function test_is_watch_paths_triggered_with_character_set_pattern()
|
||||
{
|
||||
$application = new Application();
|
||||
$application->watch_paths = "[abc]test.txt\nfile[0-9].yml";
|
||||
|
||||
// Character set matches
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['atest.txt'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['btest.txt'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['ctest.txt'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['file1.yml'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['file9.yml'])));
|
||||
|
||||
// Non-matching files
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['dtest.txt'])));
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['test.txt'])));
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['fileA.yml'])));
|
||||
}
|
||||
|
||||
public function test_is_watch_paths_triggered_with_empty_watch_paths()
|
||||
{
|
||||
$application = new Application();
|
||||
$application->watch_paths = '';
|
||||
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['any-file.txt'])));
|
||||
}
|
||||
|
||||
public function test_is_watch_paths_triggered_with_whitespace_only_patterns()
|
||||
{
|
||||
$application = new Application();
|
||||
$application->watch_paths = "\n \n\t\n";
|
||||
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['any-file.txt'])));
|
||||
}
|
||||
|
||||
public function test_is_watch_paths_triggered_for_dockercompose_typical_patterns()
|
||||
{
|
||||
$application = new Application();
|
||||
$application->watch_paths = "docker-compose*.yml\n.env*\nDockerfile*\nservices/**";
|
||||
|
||||
// Docker Compose related files
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['docker-compose.yml'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['docker-compose.prod.yml'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['docker-compose-dev.yml'])));
|
||||
|
||||
// Environment files
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['.env'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['.env.local'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['.env.production'])));
|
||||
|
||||
// Dockerfile variations
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['Dockerfile'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['Dockerfile.prod'])));
|
||||
|
||||
// Service files
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['services/api/app.js'])));
|
||||
$this->assertTrue($application->isWatchPathsTriggered(collect(['services/web/index.html'])));
|
||||
|
||||
// Non-matching files (e.g., documentation, configs outside services)
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['README.md'])));
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['package.json'])));
|
||||
$this->assertFalse($application->isWatchPathsTriggered(collect(['config/nginx.conf'])));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue