diff --git a/app/Models/Application.php b/app/Models/Application.php index 910617f7c7..bc59218bce 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -1507,7 +1507,7 @@ class Application extends BaseModel $previousSnapshot = []; } - return app(ConfigurationDiffer::class)->diff($previousSnapshot, $currentSnapshot); + return app(ConfigurationDiffer::class)->diff(ApplicationConfigurationSnapshot::withoutPreviewVolumes($previousSnapshot, $this), $currentSnapshot); } public function hasPendingDeploymentConfigurationChanges(): bool @@ -1551,7 +1551,7 @@ class Application extends BaseModel 'configuration_hash' => $hash, 'configuration_snapshot' => $snapshot, 'configuration_diff' => $previousDeployment?->configuration_snapshot - ? app(ConfigurationDiffer::class)->diff($previousDeployment->configuration_snapshot, $snapshot)->toArray() + ? app(ConfigurationDiffer::class)->diff(ApplicationConfigurationSnapshot::withoutPreviewVolumes($previousDeployment->configuration_snapshot, $this), $snapshot)->toArray() : null, ]); diff --git a/app/Services/DeploymentConfiguration/ApplicationConfigurationSnapshot.php b/app/Services/DeploymentConfiguration/ApplicationConfigurationSnapshot.php index b632a1cedc..7ae07245ad 100644 --- a/app/Services/DeploymentConfiguration/ApplicationConfigurationSnapshot.php +++ b/app/Services/DeploymentConfiguration/ApplicationConfigurationSnapshot.php @@ -100,6 +100,36 @@ class ApplicationConfigurationSnapshot ]; } + /** + * Removes the volumes of Docker Compose preview deployments from a snapshot. Older snapshots + * can contain them, because the parser stores preview volumes on the application. + * + * @param array $snapshot + * @return array + */ + public static function withoutPreviewVolumes(array $snapshot, Application $application): array + { + if (! is_array(data_get($snapshot, 'sections.storage.items'))) { + return $snapshot; + } + + $snapshot['sections']['storage']['items'] = collect($snapshot['sections']['storage']['items']) + ->reject(fn (array $item): bool => str_starts_with((string) data_get($item, 'key'), 'volume_') + && self::isPreviewVolumeName((string) data_get($item, 'compare_value.source'), $application)) + ->values() + ->all(); + + return $snapshot; + } + + /** + * The compose parser names preview volumes "{uuid}_{volume}-pr-{id}" ("{uuid}-{volume}-pr-{id}" before parser version 3). + */ + private static function isPreviewVolumeName(string $name, Application $application): bool + { + return preg_match('/^'.preg_quote($application->uuid, '/').'[_-].+-pr-\d+$/', $name) === 1; + } + /** * @return array> */ @@ -237,6 +267,7 @@ class ApplicationConfigurationSnapshot $volumes = $this->application->persistentStorages() ->orderBy('id') ->get(['id', 'name', 'mount_path', 'host_path']) + ->reject(fn (LocalPersistentVolume $volume): bool => blank($volume->host_path) && self::isPreviewVolumeName($volume->name, $this->application)) ->map(function (LocalPersistentVolume $volume): array { $source = $volume->host_path ?: $volume->name; diff --git a/bootstrap/helpers/parsers.php b/bootstrap/helpers/parsers.php index 09c2b69422..6b8b133f01 100644 --- a/bootstrap/helpers/parsers.php +++ b/bootstrap/helpers/parsers.php @@ -2018,6 +2018,10 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int $serviceEnvKey = str(normalizeComposeServiceName((string) $forServiceName))->upper(); $coolifyEnvironments->put('SERVICE_URL_'.$serviceEnvKey, $coolifyUrl->__toString()); $coolifyEnvironments->put('SERVICE_FQDN_'.$serviceEnvKey, $coolifyFqdn); + // A preview uses its own domains; the stored variables belong to the main application. + if ($isPullRequest) { + continue; + } $resource->environment_variables()->updateOrCreate([ 'resourceable_type' => Application::class, 'resourceable_id' => $resource->id, @@ -2034,7 +2038,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int 'value' => $coolifyFqdn, 'is_preview' => false, ]); - } else { + } elseif (! $isPullRequest) { $resource->environment_variables()->where('resourceable_type', Application::class) ->where('resourceable_id', $resource->id) ->where('key', 'LIKE', "SERVICE_FQDN_{$serviceNameFormatted}%") @@ -2099,7 +2103,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int if ($environment->count() > 0) { $environment = $environment->filter(function ($value, $key) { return ! str($key)->startsWith('SERVICE_FQDN_'); - })->map(function ($value, $key) use ($resource) { + })->map(function ($value, $key) use ($resource, $isPullRequest) { // Preserve empty strings and null values with correct Docker Compose semantics: // - Empty string: Variable is set to "" (e.g., HTTP_PROXY="" means "no proxy") // - Null: Variable is unset/removed from container environment (may inherit from host) @@ -2107,8 +2111,9 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int // User explicitly wants variable unset - respect that // NEVER override from database - null means "inherit from environment" // Keep as null (will be excluded from container environment) - } elseif ($value === '') { - // Empty string - allow database override for backward compatibility + } elseif ($value === '' && ! ($isPullRequest && str($key)->startsWith('SERVICE_URL_'))) { + // Empty string - allow database override for backward compatibility. + // The stored service URLs belong to the main application, so a preview does not use them. $dbEnv = $resource->environment_variables()->where('key', $key)->first(); // Only use database override if it exists AND has a non-empty value if ($dbEnv && str($dbEnv->value)->isNotEmpty()) { diff --git a/tests/Feature/ApplicationPreviewComposeCleanupTest.php b/tests/Feature/ApplicationPreviewComposeCleanupTest.php index dfb4f546cf..c926b35745 100644 --- a/tests/Feature/ApplicationPreviewComposeCleanupTest.php +++ b/tests/Feature/ApplicationPreviewComposeCleanupTest.php @@ -249,3 +249,17 @@ it('parses a preview with a bind mount in development mode', function () { expect(json_encode($compose, JSON_UNESCAPED_SLASHES))->toContain('/config/app.conf-pr-42'); }); + +it('keeps the production service URL variables when it parses a preview', function () { + Process::fake(); + $this->application->update(['docker_compose_domains' => json_encode(['web' => ['domain' => 'https://web.example.com']])]); + $this->application->fresh()->parse(); + $this->preview->update(['docker_compose_domains' => json_encode(['web' => ['domain' => 'https://pr-42.example.com']])]); + + $compose = $this->application->fresh()->parse(pull_request_id: 42, preview_id: $this->preview->id); + + $productionVariables = $this->application->environment_variables()->pluck('value', 'key'); + expect($productionVariables->get('SERVICE_URL_WEB'))->toBe('https://web.example.com') + ->and($productionVariables->get('SERVICE_FQDN_WEB'))->toBe('web.example.com') + ->and(json_encode($compose, JSON_UNESCAPED_SLASHES))->toContain('https://pr-42.example.com'); +}); diff --git a/tests/Fixtures/parser/expected/application-with-exact-caddy-labels.yaml b/tests/Fixtures/parser/expected/application-with-exact-caddy-labels.yaml index 1d5f6c0885..885372bac3 100644 --- a/tests/Fixtures/parser/expected/application-with-exact-caddy-labels.yaml +++ b/tests/Fixtures/parser/expected/application-with-exact-caddy-labels.yaml @@ -1386,26 +1386,26 @@ required: true SELF_REF: null SERVICE_BASE64_64_WEB: '' - SERVICE_FQDN_ADMIN: 7.admin.example.com + SERVICE_FQDN_ADMIN: referenceapplication0001.10.0.0.1.sslip.io SERVICE_FQDN_ADMIN_8080: null - SERVICE_FQDN_API: 7.www.api.example.com - SERVICE_FQDN_API_V2: 7.api-v2.example.com - SERVICE_FQDN_BLOG: 7.blog.example.com - SERVICE_FQDN_MY_APP: 7.my-app.example.com - SERVICE_FQDN_WEB: 7.web.example.com - SERVICE_FQDN_WEB_3000: null + SERVICE_FQDN_API: referenceapplication0001.10.0.0.1.sslip.io/api + SERVICE_FQDN_API_V2: api-v2.example.com + SERVICE_FQDN_BLOG: blog.example.com + SERVICE_FQDN_MY_APP: my-app.example.com + SERVICE_FQDN_WEB: web.example.com + SERVICE_FQDN_WEB_3000: 'web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_HEX_32_WEB: '' SERVICE_PASSWORD_DB: '' SERVICE_PASSWORD_NOBRACES: '' SERVICE_PASSWORD_WEB: '' - SERVICE_URL_ADMIN: 'https://7.admin.example.com' - SERVICE_URL_API: 'https://7.www.api.example.com' - SERVICE_URL_API_V2: 'https://7.api-v2.example.com' - SERVICE_URL_BLOG: 'https://7.blog.example.com/blog:2368' - SERVICE_URL_MY_APP: 'https://7.my-app.example.com' + SERVICE_URL_ADMIN: 'https://admin.example.com' + SERVICE_URL_API: 'https://www.api.example.com' + SERVICE_URL_API_V2: 'https://api-v2.example.com' + SERVICE_URL_BLOG: 'https://blog.example.com/blog:2368' + SERVICE_URL_MY_APP: 'https://my-app.example.com' SERVICE_URL_MY_APP_80: null - SERVICE_URL_WEB: 'https://7.web.example.com' - SERVICE_URL_WEB_3000: null + SERVICE_URL_WEB: 'https://web.example.com' + SERVICE_URL_WEB_3000: 'http://web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_USER_DB: '' SERVICE_USER_WEB: '' WITH_DEFAULT: null @@ -2088,23 +2088,23 @@ SERVICE_FQDN_ADMIN: referenceapplication0001.10.0.0.1.sslip.io SERVICE_FQDN_ADMIN_8080: null SERVICE_FQDN_API: referenceapplication0001.10.0.0.1.sslip.io/api - SERVICE_FQDN_API_V2: 7.api-v2.example.com - SERVICE_FQDN_BLOG: 7.blog.example.com - SERVICE_FQDN_MY_APP: 7.my-app.example.com - SERVICE_FQDN_WEB: 7.web.example.com - SERVICE_FQDN_WEB_3000: null + SERVICE_FQDN_API_V2: api-v2.example.com + SERVICE_FQDN_BLOG: blog.example.com + SERVICE_FQDN_MY_APP: my-app.example.com + SERVICE_FQDN_WEB: web.example.com + SERVICE_FQDN_WEB_3000: 'web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_HEX_32_WEB: '' SERVICE_PASSWORD_DB: '' SERVICE_PASSWORD_NOBRACES: '' SERVICE_PASSWORD_WEB: '' - SERVICE_URL_ADMIN: 'https://7.admin.example.com' - SERVICE_URL_API: 'https://7.www.api.example.com' - SERVICE_URL_API_V2: 'https://7.api-v2.example.com' - SERVICE_URL_BLOG: 'https://7.blog.example.com/blog:2368' - SERVICE_URL_MY_APP: 'https://7.my-app.example.com' + SERVICE_URL_ADMIN: 'https://admin.example.com' + SERVICE_URL_API: 'https://www.api.example.com' + SERVICE_URL_API_V2: 'https://api-v2.example.com' + SERVICE_URL_BLOG: 'https://blog.example.com/blog:2368' + SERVICE_URL_MY_APP: 'https://my-app.example.com' SERVICE_URL_MY_APP_80: null - SERVICE_URL_WEB: 'https://7.web.example.com' - SERVICE_URL_WEB_3000: null + SERVICE_URL_WEB: 'https://web.example.com' + SERVICE_URL_WEB_3000: 'http://web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_USER_DB: '' SERVICE_USER_WEB: '' WITH_DEFAULT: null diff --git a/tests/Fixtures/parser/expected/application-with-exact-traefik-labels.yaml b/tests/Fixtures/parser/expected/application-with-exact-traefik-labels.yaml index 21ed208946..bf5d8ae42f 100644 --- a/tests/Fixtures/parser/expected/application-with-exact-traefik-labels.yaml +++ b/tests/Fixtures/parser/expected/application-with-exact-traefik-labels.yaml @@ -1452,26 +1452,26 @@ required: true SELF_REF: null SERVICE_BASE64_64_WEB: '' - SERVICE_FQDN_ADMIN: 7.admin.example.com + SERVICE_FQDN_ADMIN: referenceapplication0001.10.0.0.1.sslip.io SERVICE_FQDN_ADMIN_8080: null - SERVICE_FQDN_API: 7.www.api.example.com - SERVICE_FQDN_API_V2: 7.api-v2.example.com - SERVICE_FQDN_BLOG: 7.blog.example.com - SERVICE_FQDN_MY_APP: 7.my-app.example.com - SERVICE_FQDN_WEB: 7.web.example.com - SERVICE_FQDN_WEB_3000: null + SERVICE_FQDN_API: referenceapplication0001.10.0.0.1.sslip.io/api + SERVICE_FQDN_API_V2: api-v2.example.com + SERVICE_FQDN_BLOG: blog.example.com + SERVICE_FQDN_MY_APP: my-app.example.com + SERVICE_FQDN_WEB: web.example.com + SERVICE_FQDN_WEB_3000: 'web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_HEX_32_WEB: '' SERVICE_PASSWORD_DB: '' SERVICE_PASSWORD_NOBRACES: '' SERVICE_PASSWORD_WEB: '' - SERVICE_URL_ADMIN: 'https://7.admin.example.com' - SERVICE_URL_API: 'https://7.www.api.example.com' - SERVICE_URL_API_V2: 'https://7.api-v2.example.com' - SERVICE_URL_BLOG: 'https://7.blog.example.com/blog:2368' - SERVICE_URL_MY_APP: 'https://7.my-app.example.com' + SERVICE_URL_ADMIN: 'https://admin.example.com' + SERVICE_URL_API: 'https://www.api.example.com' + SERVICE_URL_API_V2: 'https://api-v2.example.com' + SERVICE_URL_BLOG: 'https://blog.example.com/blog:2368' + SERVICE_URL_MY_APP: 'https://my-app.example.com' SERVICE_URL_MY_APP_80: null - SERVICE_URL_WEB: 'https://7.web.example.com' - SERVICE_URL_WEB_3000: null + SERVICE_URL_WEB: 'https://web.example.com' + SERVICE_URL_WEB_3000: 'http://web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_USER_DB: '' SERVICE_USER_WEB: '' WITH_DEFAULT: null @@ -2205,23 +2205,23 @@ SERVICE_FQDN_ADMIN: referenceapplication0001.10.0.0.1.sslip.io SERVICE_FQDN_ADMIN_8080: null SERVICE_FQDN_API: referenceapplication0001.10.0.0.1.sslip.io/api - SERVICE_FQDN_API_V2: 7.api-v2.example.com - SERVICE_FQDN_BLOG: 7.blog.example.com - SERVICE_FQDN_MY_APP: 7.my-app.example.com - SERVICE_FQDN_WEB: 7.web.example.com - SERVICE_FQDN_WEB_3000: null + SERVICE_FQDN_API_V2: api-v2.example.com + SERVICE_FQDN_BLOG: blog.example.com + SERVICE_FQDN_MY_APP: my-app.example.com + SERVICE_FQDN_WEB: web.example.com + SERVICE_FQDN_WEB_3000: 'web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_HEX_32_WEB: '' SERVICE_PASSWORD_DB: '' SERVICE_PASSWORD_NOBRACES: '' SERVICE_PASSWORD_WEB: '' - SERVICE_URL_ADMIN: 'https://7.admin.example.com' - SERVICE_URL_API: 'https://7.www.api.example.com' - SERVICE_URL_API_V2: 'https://7.api-v2.example.com' - SERVICE_URL_BLOG: 'https://7.blog.example.com/blog:2368' - SERVICE_URL_MY_APP: 'https://7.my-app.example.com' + SERVICE_URL_ADMIN: 'https://admin.example.com' + SERVICE_URL_API: 'https://www.api.example.com' + SERVICE_URL_API_V2: 'https://api-v2.example.com' + SERVICE_URL_BLOG: 'https://blog.example.com/blog:2368' + SERVICE_URL_MY_APP: 'https://my-app.example.com' SERVICE_URL_MY_APP_80: null - SERVICE_URL_WEB: 'https://7.web.example.com' - SERVICE_URL_WEB_3000: null + SERVICE_URL_WEB: 'https://web.example.com' + SERVICE_URL_WEB_3000: 'http://web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_USER_DB: '' SERVICE_USER_WEB: '' WITH_DEFAULT: null diff --git a/tests/Fixtures/parser/expected/application-with-settings.yaml b/tests/Fixtures/parser/expected/application-with-settings.yaml index 4958098cb2..32c3ad3a7b 100644 --- a/tests/Fixtures/parser/expected/application-with-settings.yaml +++ b/tests/Fixtures/parser/expected/application-with-settings.yaml @@ -1648,26 +1648,26 @@ required: true SELF_REF: null SERVICE_BASE64_64_WEB: '' - SERVICE_FQDN_ADMIN: 7.admin.example.com + SERVICE_FQDN_ADMIN: referenceapplication0001.10.0.0.1.sslip.io SERVICE_FQDN_ADMIN_8080: null - SERVICE_FQDN_API: 7.www.api.example.com - SERVICE_FQDN_API_V2: 7.api-v2.example.com - SERVICE_FQDN_BLOG: 7.blog.example.com - SERVICE_FQDN_MY_APP: 7.my-app.example.com - SERVICE_FQDN_WEB: 7.web.example.com - SERVICE_FQDN_WEB_3000: null + SERVICE_FQDN_API: referenceapplication0001.10.0.0.1.sslip.io/api + SERVICE_FQDN_API_V2: api-v2.example.com + SERVICE_FQDN_BLOG: blog.example.com + SERVICE_FQDN_MY_APP: my-app.example.com + SERVICE_FQDN_WEB: web.example.com + SERVICE_FQDN_WEB_3000: 'web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_HEX_32_WEB: '' SERVICE_PASSWORD_DB: '' SERVICE_PASSWORD_NOBRACES: '' SERVICE_PASSWORD_WEB: '' - SERVICE_URL_ADMIN: 'https://7.admin.example.com' - SERVICE_URL_API: 'https://7.www.api.example.com' - SERVICE_URL_API_V2: 'https://7.api-v2.example.com' - SERVICE_URL_BLOG: 'https://7.blog.example.com/blog:2368' - SERVICE_URL_MY_APP: 'https://7.my-app.example.com' + SERVICE_URL_ADMIN: 'https://admin.example.com' + SERVICE_URL_API: 'https://www.api.example.com' + SERVICE_URL_API_V2: 'https://api-v2.example.com' + SERVICE_URL_BLOG: 'https://blog.example.com/blog:2368' + SERVICE_URL_MY_APP: 'https://my-app.example.com' SERVICE_URL_MY_APP_80: null - SERVICE_URL_WEB: 'https://7.web.example.com' - SERVICE_URL_WEB_3000: null + SERVICE_URL_WEB: 'https://web.example.com' + SERVICE_URL_WEB_3000: 'http://web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_USER_DB: '' SERVICE_USER_WEB: '' WITH_DEFAULT: null @@ -2511,23 +2511,23 @@ SERVICE_FQDN_ADMIN: referenceapplication0001.10.0.0.1.sslip.io SERVICE_FQDN_ADMIN_8080: null SERVICE_FQDN_API: referenceapplication0001.10.0.0.1.sslip.io/api - SERVICE_FQDN_API_V2: 7.api-v2.example.com - SERVICE_FQDN_BLOG: 7.blog.example.com - SERVICE_FQDN_MY_APP: 7.my-app.example.com - SERVICE_FQDN_WEB: 7.web.example.com - SERVICE_FQDN_WEB_3000: null + SERVICE_FQDN_API_V2: api-v2.example.com + SERVICE_FQDN_BLOG: blog.example.com + SERVICE_FQDN_MY_APP: my-app.example.com + SERVICE_FQDN_WEB: web.example.com + SERVICE_FQDN_WEB_3000: 'web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_HEX_32_WEB: '' SERVICE_PASSWORD_DB: '' SERVICE_PASSWORD_NOBRACES: '' SERVICE_PASSWORD_WEB: '' - SERVICE_URL_ADMIN: 'https://7.admin.example.com' - SERVICE_URL_API: 'https://7.www.api.example.com' - SERVICE_URL_API_V2: 'https://7.api-v2.example.com' - SERVICE_URL_BLOG: 'https://7.blog.example.com/blog:2368' - SERVICE_URL_MY_APP: 'https://7.my-app.example.com' + SERVICE_URL_ADMIN: 'https://admin.example.com' + SERVICE_URL_API: 'https://www.api.example.com' + SERVICE_URL_API_V2: 'https://api-v2.example.com' + SERVICE_URL_BLOG: 'https://blog.example.com/blog:2368' + SERVICE_URL_MY_APP: 'https://my-app.example.com' SERVICE_URL_MY_APP_80: null - SERVICE_URL_WEB: 'https://7.web.example.com' - SERVICE_URL_WEB_3000: null + SERVICE_URL_WEB: 'https://web.example.com' + SERVICE_URL_WEB_3000: 'http://web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_USER_DB: '' SERVICE_USER_WEB: '' WITH_DEFAULT: null diff --git a/tests/Fixtures/parser/expected/application.yaml b/tests/Fixtures/parser/expected/application.yaml index eb5c2be0b0..6e52c2b0ee 100644 --- a/tests/Fixtures/parser/expected/application.yaml +++ b/tests/Fixtures/parser/expected/application.yaml @@ -1530,26 +1530,26 @@ required: true SELF_REF: null SERVICE_BASE64_64_WEB: '' - SERVICE_FQDN_ADMIN: 7.admin.example.com + SERVICE_FQDN_ADMIN: referenceapplication0001.10.0.0.1.sslip.io SERVICE_FQDN_ADMIN_8080: null - SERVICE_FQDN_API: 7.www.api.example.com - SERVICE_FQDN_API_V2: 7.api-v2.example.com - SERVICE_FQDN_BLOG: 7.blog.example.com - SERVICE_FQDN_MY_APP: 7.my-app.example.com - SERVICE_FQDN_WEB: 7.web.example.com - SERVICE_FQDN_WEB_3000: null + SERVICE_FQDN_API: referenceapplication0001.10.0.0.1.sslip.io/api + SERVICE_FQDN_API_V2: api-v2.example.com + SERVICE_FQDN_BLOG: blog.example.com + SERVICE_FQDN_MY_APP: my-app.example.com + SERVICE_FQDN_WEB: web.example.com + SERVICE_FQDN_WEB_3000: 'web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_HEX_32_WEB: '' SERVICE_PASSWORD_DB: '' SERVICE_PASSWORD_NOBRACES: '' SERVICE_PASSWORD_WEB: '' - SERVICE_URL_ADMIN: 'https://7.admin.example.com' - SERVICE_URL_API: 'https://7.www.api.example.com' - SERVICE_URL_API_V2: 'https://7.api-v2.example.com' - SERVICE_URL_BLOG: 'https://7.blog.example.com/blog:2368' - SERVICE_URL_MY_APP: 'https://7.my-app.example.com' + SERVICE_URL_ADMIN: 'https://admin.example.com' + SERVICE_URL_API: 'https://www.api.example.com' + SERVICE_URL_API_V2: 'https://api-v2.example.com' + SERVICE_URL_BLOG: 'https://blog.example.com/blog:2368' + SERVICE_URL_MY_APP: 'https://my-app.example.com' SERVICE_URL_MY_APP_80: null - SERVICE_URL_WEB: 'https://7.web.example.com' - SERVICE_URL_WEB_3000: null + SERVICE_URL_WEB: 'https://web.example.com' + SERVICE_URL_WEB_3000: 'http://web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_USER_DB: '' SERVICE_USER_WEB: '' WITH_DEFAULT: null @@ -2331,23 +2331,23 @@ SERVICE_FQDN_ADMIN: referenceapplication0001.10.0.0.1.sslip.io SERVICE_FQDN_ADMIN_8080: null SERVICE_FQDN_API: referenceapplication0001.10.0.0.1.sslip.io/api - SERVICE_FQDN_API_V2: 7.api-v2.example.com - SERVICE_FQDN_BLOG: 7.blog.example.com - SERVICE_FQDN_MY_APP: 7.my-app.example.com - SERVICE_FQDN_WEB: 7.web.example.com - SERVICE_FQDN_WEB_3000: null + SERVICE_FQDN_API_V2: api-v2.example.com + SERVICE_FQDN_BLOG: blog.example.com + SERVICE_FQDN_MY_APP: my-app.example.com + SERVICE_FQDN_WEB: web.example.com + SERVICE_FQDN_WEB_3000: 'web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_HEX_32_WEB: '' SERVICE_PASSWORD_DB: '' SERVICE_PASSWORD_NOBRACES: '' SERVICE_PASSWORD_WEB: '' - SERVICE_URL_ADMIN: 'https://7.admin.example.com' - SERVICE_URL_API: 'https://7.www.api.example.com' - SERVICE_URL_API_V2: 'https://7.api-v2.example.com' - SERVICE_URL_BLOG: 'https://7.blog.example.com/blog:2368' - SERVICE_URL_MY_APP: 'https://7.my-app.example.com' + SERVICE_URL_ADMIN: 'https://admin.example.com' + SERVICE_URL_API: 'https://www.api.example.com' + SERVICE_URL_API_V2: 'https://api-v2.example.com' + SERVICE_URL_BLOG: 'https://blog.example.com/blog:2368' + SERVICE_URL_MY_APP: 'https://my-app.example.com' SERVICE_URL_MY_APP_80: null - SERVICE_URL_WEB: 'https://7.web.example.com' - SERVICE_URL_WEB_3000: null + SERVICE_URL_WEB: 'https://web.example.com' + SERVICE_URL_WEB_3000: 'http://web-referenceapplication0001.10.0.0.1.sslip.io:3000' SERVICE_USER_DB: '' SERVICE_USER_WEB: '' WITH_DEFAULT: null diff --git a/tests/Unit/DeploymentConfiguration/ApplicationConfigurationSnapshotTest.php b/tests/Unit/DeploymentConfiguration/ApplicationConfigurationSnapshotTest.php index f4b4586832..ed5e43e592 100644 --- a/tests/Unit/DeploymentConfiguration/ApplicationConfigurationSnapshotTest.php +++ b/tests/Unit/DeploymentConfiguration/ApplicationConfigurationSnapshotTest.php @@ -406,3 +406,25 @@ it('describes added unlocked environment variables with their value', function ( ->and($change['new_display_value'])->toBe('new-secret') ->and(json_encode($diff->toArray()))->toContain('new-secret'); }); + +it('does not report Docker Compose preview volumes as pending production changes', function () { + $application = snapshotTestApplication(['build_pack' => 'dockercompose']); + $previewVolume = fn (): LocalPersistentVolume => LocalPersistentVolume::create([ + 'name' => $application->uuid.'_data-pr-42', + 'mount_path' => '/app/data', + 'resource_id' => $application->id, + 'resource_type' => $application->getMorphClass(), + ]); + + // A preview deployment adds its volume after the production deployment. + markSnapshotTestApplicationDeployed($application); + $volume = $previewVolume(); + expect($application->refresh()->pendingDeploymentConfigurationDiff()->isChanged())->toBeFalse(); + + // An older production snapshot contains the volume, and the preview cleanup removes it. + $volume->delete(); + $previewVolume(); + markSnapshotTestApplicationDeployed($application); + LocalPersistentVolume::query()->where('name', $application->uuid.'_data-pr-42')->delete(); + expect($application->refresh()->pendingDeploymentConfigurationDiff()->isChanged())->toBeFalse(); +});