fix: normalize preview paths and use BUILD_TIME_ENV_PATH constant
- Fix double-slash issue in Docker Compose preview paths when baseDirectory is "/" - Normalize baseDirectory using rtrim() to prevent path concatenation issues - Replace hardcoded '/artifacts/build-time.env' with ApplicationDeploymentJob::BUILD_TIME_ENV_PATH - Make BUILD_TIME_ENV_PATH constant public for reusability - Add comprehensive unit tests (11 test cases, 25 assertions) Fixes preview path generation in: - getDockerComposeBuildCommandPreviewProperty() - getDockerComposeStartCommandPreviewProperty() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
2eeb2b94ec
commit
cfe3f5d8b9
3 changed files with 167 additions and 4 deletions
|
|
@ -41,7 +41,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, EnvironmentVariableAnalyzer, ExecuteRemoteCommand, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, EnvironmentVariableAnalyzer, ExecuteRemoteCommand, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
private const BUILD_TIME_ENV_PATH = '/artifacts/build-time.env';
|
public const BUILD_TIME_ENV_PATH = '/artifacts/build-time.env';
|
||||||
|
|
||||||
private const BUILD_SCRIPT_PATH = '/artifacts/build.sh';
|
private const BUILD_SCRIPT_PATH = '/artifacts/build.sh';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1012,12 +1012,16 @@ public function getDockerComposeBuildCommandPreviewProperty(): string
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize baseDirectory to prevent double slashes (e.g., when baseDirectory is '/')
|
||||||
|
$normalizedBase = $this->baseDirectory === '/' ? '' : rtrim($this->baseDirectory, '/');
|
||||||
|
|
||||||
// Use relative path for clarity in preview (e.g., ./backend/docker-compose.yaml)
|
// Use relative path for clarity in preview (e.g., ./backend/docker-compose.yaml)
|
||||||
// Actual deployment uses absolute path: /artifacts/{deployment_uuid}{base_directory}{docker_compose_location}
|
// Actual deployment uses absolute path: /artifacts/{deployment_uuid}{base_directory}{docker_compose_location}
|
||||||
|
// Build-time env path references ApplicationDeploymentJob::BUILD_TIME_ENV_PATH as source of truth
|
||||||
return injectDockerComposeFlags(
|
return injectDockerComposeFlags(
|
||||||
$this->dockerComposeCustomBuildCommand,
|
$this->dockerComposeCustomBuildCommand,
|
||||||
".{$this->baseDirectory}{$this->dockerComposeLocation}",
|
".{$normalizedBase}{$this->dockerComposeLocation}",
|
||||||
'/artifacts/build-time.env'
|
\App\Jobs\ApplicationDeploymentJob::BUILD_TIME_ENV_PATH
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1027,11 +1031,14 @@ public function getDockerComposeStartCommandPreviewProperty(): string
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize baseDirectory to prevent double slashes (e.g., when baseDirectory is '/')
|
||||||
|
$normalizedBase = $this->baseDirectory === '/' ? '' : rtrim($this->baseDirectory, '/');
|
||||||
|
|
||||||
// Use relative path for clarity in preview (e.g., ./backend/docker-compose.yaml)
|
// Use relative path for clarity in preview (e.g., ./backend/docker-compose.yaml)
|
||||||
// Placeholder {workdir}/.env shows it's the workdir .env file (runtime env, not build-time)
|
// Placeholder {workdir}/.env shows it's the workdir .env file (runtime env, not build-time)
|
||||||
return injectDockerComposeFlags(
|
return injectDockerComposeFlags(
|
||||||
$this->dockerComposeCustomStartCommand,
|
$this->dockerComposeCustomStartCommand,
|
||||||
".{$this->baseDirectory}{$this->dockerComposeLocation}",
|
".{$normalizedBase}{$this->dockerComposeLocation}",
|
||||||
'{workdir}/.env'
|
'{workdir}/.env'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
156
tests/Unit/Livewire/ApplicationGeneralPreviewTest.php
Normal file
156
tests/Unit/Livewire/ApplicationGeneralPreviewTest.php
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Jobs\ApplicationDeploymentJob;
|
||||||
|
use App\Livewire\Project\Application\General;
|
||||||
|
|
||||||
|
it('prevents double slashes in build command preview when baseDirectory is root', function () {
|
||||||
|
// Mock the component with properties
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.yaml';
|
||||||
|
$component->dockerComposeCustomBuildCommand = 'docker compose build';
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeBuildCommandPreviewProperty();
|
||||||
|
|
||||||
|
// Should be ./docker-compose.yaml, NOT .//docker-compose.yaml
|
||||||
|
expect($preview)
|
||||||
|
->toBeString()
|
||||||
|
->toContain('./docker-compose.yaml')
|
||||||
|
->not->toContain('.//');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly formats build command preview with nested baseDirectory', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/backend';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.yaml';
|
||||||
|
$component->dockerComposeCustomBuildCommand = 'docker compose build';
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeBuildCommandPreviewProperty();
|
||||||
|
|
||||||
|
// Should be ./backend/docker-compose.yaml
|
||||||
|
expect($preview)
|
||||||
|
->toBeString()
|
||||||
|
->toContain('./backend/docker-compose.yaml');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly formats build command preview with deeply nested baseDirectory', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/apps/api/backend';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.prod.yaml';
|
||||||
|
$component->dockerComposeCustomBuildCommand = 'docker compose build';
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeBuildCommandPreviewProperty();
|
||||||
|
|
||||||
|
expect($preview)
|
||||||
|
->toBeString()
|
||||||
|
->toContain('./apps/api/backend/docker-compose.prod.yaml');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses BUILD_TIME_ENV_PATH constant instead of hardcoded path in build command preview', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.yaml';
|
||||||
|
$component->dockerComposeCustomBuildCommand = 'docker compose build';
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeBuildCommandPreviewProperty();
|
||||||
|
|
||||||
|
// Should contain the path from the constant
|
||||||
|
expect($preview)
|
||||||
|
->toBeString()
|
||||||
|
->toContain(ApplicationDeploymentJob::BUILD_TIME_ENV_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string for build command preview when no custom build command is set', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/backend';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.yaml';
|
||||||
|
$component->dockerComposeCustomBuildCommand = null;
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeBuildCommandPreviewProperty();
|
||||||
|
|
||||||
|
expect($preview)->toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prevents double slashes in start command preview when baseDirectory is root', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.yaml';
|
||||||
|
$component->dockerComposeCustomStartCommand = 'docker compose up -d';
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeStartCommandPreviewProperty();
|
||||||
|
|
||||||
|
// Should be ./docker-compose.yaml, NOT .//docker-compose.yaml
|
||||||
|
expect($preview)
|
||||||
|
->toBeString()
|
||||||
|
->toContain('./docker-compose.yaml')
|
||||||
|
->not->toContain('.//');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly formats start command preview with nested baseDirectory', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/frontend';
|
||||||
|
$component->dockerComposeLocation = '/compose.yaml';
|
||||||
|
$component->dockerComposeCustomStartCommand = 'docker compose up -d';
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeStartCommandPreviewProperty();
|
||||||
|
|
||||||
|
expect($preview)
|
||||||
|
->toBeString()
|
||||||
|
->toContain('./frontend/compose.yaml');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses workdir env placeholder in start command preview', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.yaml';
|
||||||
|
$component->dockerComposeCustomStartCommand = 'docker compose up -d';
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeStartCommandPreviewProperty();
|
||||||
|
|
||||||
|
// Start command should use {workdir}/.env, not build-time env
|
||||||
|
expect($preview)
|
||||||
|
->toBeString()
|
||||||
|
->toContain('{workdir}/.env')
|
||||||
|
->not->toContain(ApplicationDeploymentJob::BUILD_TIME_ENV_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string for start command preview when no custom start command is set', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/backend';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.yaml';
|
||||||
|
$component->dockerComposeCustomStartCommand = null;
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeStartCommandPreviewProperty();
|
||||||
|
|
||||||
|
expect($preview)->toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles baseDirectory with trailing slash correctly in build command', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/backend/';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.yaml';
|
||||||
|
$component->dockerComposeCustomBuildCommand = 'docker compose build';
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeBuildCommandPreviewProperty();
|
||||||
|
|
||||||
|
// rtrim should remove trailing slash to prevent double slashes
|
||||||
|
expect($preview)
|
||||||
|
->toBeString()
|
||||||
|
->toContain('./backend/docker-compose.yaml')
|
||||||
|
->not->toContain('backend//');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles baseDirectory with trailing slash correctly in start command', function () {
|
||||||
|
$component = Mockery::mock(General::class)->makePartial();
|
||||||
|
$component->baseDirectory = '/backend/';
|
||||||
|
$component->dockerComposeLocation = '/docker-compose.yaml';
|
||||||
|
$component->dockerComposeCustomStartCommand = 'docker compose up -d';
|
||||||
|
|
||||||
|
$preview = $component->getDockerComposeStartCommandPreviewProperty();
|
||||||
|
|
||||||
|
// rtrim should remove trailing slash to prevent double slashes
|
||||||
|
expect($preview)
|
||||||
|
->toBeString()
|
||||||
|
->toContain('./backend/docker-compose.yaml')
|
||||||
|
->not->toContain('backend//');
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue