Merge branch 'next' into v4.x
This commit is contained in:
commit
59f3220d26
11 changed files with 795 additions and 160 deletions
144
CHANGELOG.md
144
CHANGELOG.md
|
|
@ -4,6 +4,150 @@ # Changelog
|
|||
|
||||
## [unreleased]
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- Update changelog
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(docker)* Add a blank line for improved readability in Dockerfile
|
||||
|
||||
## [4.0.0-beta.428] - 2025-09-15
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(deployment)* Enhance deployment status reporting with detailed information on active deployments and team members
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(application)* Improve watch paths handling by trimming and filtering empty paths to prevent unnecessary triggers
|
||||
|
||||
### 🚜 Refactor
|
||||
|
||||
- *(deployment)* Streamline environment variable handling for dockercompose and improve sorting of runtime variables
|
||||
- *(remoteProcess)* Remove command log comments for file transfers to simplify code
|
||||
- *(remoteProcess)* Remove file transfer handling from remote_process and instant_remote_process functions to simplify code
|
||||
- *(deployment)* Update environment file paths in docker compose commands to use working directory for improved consistency
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- Update changelog
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(constants)* Update realtime_version from 1.0.10 to 1.0.11
|
||||
- *(versions)* Increment coolify version to 4.0.0-beta.428 and update realtime_version to 1.0.10
|
||||
|
||||
## [4.0.0-beta.427] - 2025-09-15
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(command)* Add option to sync GitHub releases to BunnyCDN and refactor sync logic
|
||||
- *(ui)* Display current version in settings dropdown and update UI accordingly
|
||||
- *(settings)* Add option to restrict PR deployments to repository members and contributors
|
||||
- *(command)* Implement SSH command retry logic with exponential backoff and logging for better error handling
|
||||
- *(ssh)* Add Sentry tracking for SSH retry events to enhance error monitoring
|
||||
- *(exceptions)* Introduce NonReportableException to handle known errors and update Handler for selective reporting
|
||||
- *(sudo-helper)* Add helper functions for command parsing and ownership management with sudo
|
||||
- *(dev-command)* Dispatch CheckHelperImageJob during instance initialization to enhance setup process
|
||||
- *(ssh-multiplexing)* Enhance multiplexed connection management with health checks and metadata caching
|
||||
- *(ssh-multiplexing)* Add connection age metadata handling to improve multiplexed connection management
|
||||
- *(database-backup)* Enhance error handling and output management in DatabaseBackupJob
|
||||
- *(application)* Display parsing version in development mode and clean up domain conflict modal markup
|
||||
- *(deployment)* Add SERVICE_NAME variables for service discovery
|
||||
- *(storages)* Add method to retrieve the first storage ID for improved stability in storage display
|
||||
- *(environment)* Add 'is_literal' attribute to environment variable for enhanced configuration options
|
||||
- *(pre-commit)* Automate generation of service templates and OpenAPI documentation during pre-commit hook
|
||||
- *(execute-container)* Enhance container command form with auto-connect feature for single container scenarios
|
||||
- *(environment)* Introduce 'is_buildtime_only' attribute to environment variables for improved build-time configuration
|
||||
- *(templates)* Add n8n service with PostgreSQL and worker support for enhanced workflow automation
|
||||
- *(user-management)* Implement user deletion command with phased resource and subscription cancellation, including dry run option
|
||||
- *(sentinel)* Add support for custom Docker images in StartSentinel and related methods
|
||||
- *(sentinel)* Add slide-over for viewing Sentinel logs and custom Docker image input for development
|
||||
- *(executions)* Add 'Load All' button to view all logs and implement loadAllLogs method for complete log retrieval
|
||||
- *(auth)* Enhance user login flow to handle team invitations, attaching users to invited teams upon first login and maintaining personal team logic for regular logins
|
||||
- *(laravel-boost)* Add Laravel Boost guidelines and MCP server configuration to enhance development experience
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(ui)* Transactional email settings link on members page (#6491)
|
||||
- *(api)* Add custom labels generation for applications with readonly container label setting enabled
|
||||
- *(ui)* Add cursor pointer to upgrade button for better user interaction
|
||||
- *(templates)* Update SECRET_KEY environment variable in getoutline.yaml to use SERVICE_HEX_32_OUTLINE
|
||||
- *(command)* Enhance database deletion command to support multiple database types
|
||||
- *(command)* Enhance cleanup process for stuck application previews by adding force delete for trashed records
|
||||
- *(user)* Ensure email attributes are stored in lowercase for consistency and prevent case-related issues
|
||||
- *(webhook)* Replace delete with forceDelete for application previews to ensure immediate removal
|
||||
- *(ssh)* Introduce SshRetryHandler and SshRetryable trait for enhanced SSH command retry logic with exponential backoff and error handling
|
||||
- Appwrite template - 500 errors, missing env vars etc.
|
||||
- *(LocalFileVolume)* Add missing directory creation command for workdir in saveStorageOnServer method
|
||||
- *(ScheduledTaskJob)* Replace generic Exception with NonReportableException for better error handling
|
||||
- *(web-routes)* Enhance backup response messages to clarify local and S3 availability
|
||||
- *(proxy)* Replace CheckConfiguration with GetProxyConfiguration and SaveConfiguration with SaveProxyConfiguration for improved clarity and consistency in proxy management
|
||||
- *(private-key)* Implement transaction handling and error verification for private key storage operations
|
||||
- *(deployment)* Add COOLIFY_* environment variables to Nixpacks build context for enhanced deployment configuration
|
||||
- *(application)* Add functionality to stop and remove Docker containers on server
|
||||
- *(templates)* Update 'compose' configuration for Appwrite service to enhance compatibility and streamline deployment
|
||||
- *(security)* Update contact email for reporting vulnerabilities to enhance privacy
|
||||
- *(feedback)* Update feedback email address to improve communication with users
|
||||
- *(security)* Update contact email for vulnerability reports to improve security communication
|
||||
- *(navbar)* Restrict subscription link visibility to admin users in cloud environment
|
||||
- *(docker)* Enhance container status aggregation for multi-container applications, including exclusion handling based on docker-compose configuration
|
||||
|
||||
### 🚜 Refactor
|
||||
|
||||
- *(jobs)* Pull github changelogs from cdn instead of github
|
||||
- *(command)* Streamline database deletion process to handle multiple database types and improve user experience
|
||||
- *(command)* Improve database collection logic for deletion command by using unique identifiers and enhancing user experience
|
||||
- *(command)* Remove InitChangelog command as it is no longer needed
|
||||
- *(command)* Streamline Init command by removing unnecessary options and enhancing error handling for various operations
|
||||
- *(webhook)* Replace direct forceDelete calls with DeleteResourceJob dispatch for application previews
|
||||
- *(command)* Replace forceDelete calls with DeleteResourceJob dispatch for all stuck resources in cleanup process
|
||||
- *(command)* Simplify SSH command retry logic by removing unnecessary logging and improving delay calculation
|
||||
- *(ssh)* Enhance error handling in SSH command execution and improve connection validation logging
|
||||
- *(backlog)* Remove outdated guidelines and project manager agent files to streamline task management documentation
|
||||
- *(error-handling)* Remove ray debugging statements from CheckUpdates and shared helper functions to clean up error reporting
|
||||
- *(file-transfer)* Replace base64 encoding with direct file transfer method across multiple database actions for improved clarity and efficiency
|
||||
- *(remoteProcess)* Remove debugging statement from transfer_file_to_server function to clean up code
|
||||
- *(dns-validation)* Rename DNS validation functions for consistency and clarity, and remove unused code
|
||||
- *(file-transfer)* Replace base64 encoding with direct file transfer method in various components for improved clarity and efficiency
|
||||
- *(private-key)* Remove debugging statement from storeInFileSystem method for cleaner code
|
||||
- *(github-webhook)* Restructure application processing by grouping applications by server for improved deployment handling
|
||||
- *(deployment)* Enhance queuing logic to support concurrent deployments by including pull request ID in checks
|
||||
- *(remoteProcess)* Remove debugging statement from transfer_file_to_container function for cleaner code
|
||||
- *(deployment)* Streamline next deployment queuing logic by repositioning queue_next_deployment call
|
||||
- *(deployment)* Add validation for pull request existence in deployment process to enhance error handling
|
||||
- *(database)* Remove volume_configuration_dir and streamline configuration directory usage in MongoDB and PostgreSQL handlers
|
||||
- *(application-source)* Improve layout and accessibility of Git repository links in the application source view
|
||||
- *(models)* Remove 'is_readonly' attribute from multiple database models for consistency
|
||||
- *(webhook)* Remove Webhook model and related logic; add migrations to drop webhooks and kubernetes tables
|
||||
- *(clone)* Consolidate application cloning logic into a dedicated function for improved maintainability and readability
|
||||
- *(clone)* Integrate preview cloning logic directly into application cloning function for improved clarity and maintainability
|
||||
- *(application)* Enhance environment variable retrieval in configuration change check for improved accuracy
|
||||
- *(clone)* Enhance application cloning by separating production and preview environment variable handling
|
||||
- *(deployment)* Add environment variable copying logic to Docker build commands for pull requests
|
||||
- *(environment)* Standardize service name formatting by replacing '-' and '.' with '_' in environment variable keys
|
||||
- *(deployment)* Update environment file handling in Docker commands to use '/artifacts/' path and streamline variable management
|
||||
- *(openapi)* Remove 'is_build_time' attribute from environment variable definitions to streamline configuration
|
||||
- *(environment)* Remove 'is_build_time' attribute from environment variable handling across the application to simplify configuration
|
||||
- *(environment)* Streamline environment variable handling by replacing sorting methods with direct property access and enhancing query ordering for improved performance
|
||||
- *(stripe-jobs)* Comment out internal notification calls and add subscription status verification before sending failure notifications
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- Update changelog
|
||||
- *(testing-patterns)* Add important note to always run tests inside the `coolify` container for clarity
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- Update coolify version to 4.0.0-beta.427 and nightly version to 4.0.0-beta.428
|
||||
- Use main value then fallback to service_ values
|
||||
- Remove webhooks table cleanup
|
||||
- *(cleanup)* Remove deprecated ServerCheck and related job classes to streamline codebase
|
||||
- *(versions)* Update sentinel version from 0.0.15 to 0.0.16 in versions.json files
|
||||
|
||||
## [4.0.0-beta.426] - 2025-08-28
|
||||
|
||||
### 🚜 Refactor
|
||||
|
||||
- *(policy)* Simplify ServiceDatabasePolicy methods to always return true and add manageBackups method
|
||||
|
|
|
|||
|
|
@ -167,6 +167,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
|
||||
private bool $preserveRepository = false;
|
||||
|
||||
private bool $dockerBuildkitSupported = false;
|
||||
|
||||
private Collection|string $build_secrets;
|
||||
|
||||
public function tags()
|
||||
{
|
||||
// Do not remove this one, it needs to properly identify which worker is running the job
|
||||
|
|
@ -183,6 +187,7 @@ public function __construct(public int $application_deployment_queue_id)
|
|||
$this->application = Application::find($this->application_deployment_queue->application_id);
|
||||
$this->build_pack = data_get($this->application, 'build_pack');
|
||||
$this->build_args = collect([]);
|
||||
$this->build_secrets = '';
|
||||
|
||||
$this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
|
||||
$this->pull_request_id = $this->application_deployment_queue->pull_request_id;
|
||||
|
|
@ -250,6 +255,14 @@ public function __construct(public int $application_deployment_queue_id)
|
|||
|
||||
public function handle(): void
|
||||
{
|
||||
// Check if deployment was cancelled before we even started
|
||||
$this->application_deployment_queue->refresh();
|
||||
if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
|
||||
$this->application_deployment_queue->addLogEntry('Deployment was cancelled before starting.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->application_deployment_queue->update([
|
||||
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
|
||||
'horizon_job_worker' => gethostname(),
|
||||
|
|
@ -263,7 +276,6 @@ public function handle(): void
|
|||
try {
|
||||
// Make sure the private key is stored in the filesystem
|
||||
$this->server->privateKey->storeInFileSystem();
|
||||
|
||||
// Generate custom host<->ip mapping
|
||||
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
||||
|
||||
|
|
@ -319,6 +331,7 @@ public function handle(): void
|
|||
$this->build_server = $this->server;
|
||||
$this->original_server = $this->server;
|
||||
}
|
||||
$this->detectBuildKitCapabilities();
|
||||
$this->decide_what_to_do();
|
||||
} catch (Exception $e) {
|
||||
if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
|
||||
|
|
@ -336,6 +349,7 @@ public function handle(): void
|
|||
} else {
|
||||
$this->write_deployment_configurations();
|
||||
}
|
||||
|
||||
$this->application_deployment_queue->addLogEntry("Gracefully shutting down build container: {$this->deployment_uuid}");
|
||||
$this->graceful_shutdown_container($this->deployment_uuid);
|
||||
|
||||
|
|
@ -343,6 +357,80 @@ public function handle(): void
|
|||
}
|
||||
}
|
||||
|
||||
private function detectBuildKitCapabilities(): void
|
||||
{
|
||||
// If build secrets are not enabled, skip detection and use traditional args
|
||||
if (! $this->application->settings->use_build_secrets) {
|
||||
$this->dockerBuildkitSupported = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$serverToCheck = $this->use_build_server ? $this->build_server : $this->server;
|
||||
$serverName = $this->use_build_server ? "build server ({$serverToCheck->name})" : "deployment server ({$serverToCheck->name})";
|
||||
|
||||
try {
|
||||
$dockerVersion = instant_remote_process(
|
||||
["docker version --format '{{.Server.Version}}'"],
|
||||
$serverToCheck
|
||||
);
|
||||
|
||||
$versionParts = explode('.', $dockerVersion);
|
||||
$majorVersion = (int) $versionParts[0];
|
||||
$minorVersion = (int) ($versionParts[1] ?? 0);
|
||||
|
||||
if ($majorVersion < 18 || ($majorVersion == 18 && $minorVersion < 9)) {
|
||||
$this->dockerBuildkitSupported = false;
|
||||
$this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} on {$serverName} does not support BuildKit (requires 18.09+). Build secrets feature disabled.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$buildkitEnabled = instant_remote_process(
|
||||
["docker buildx version >/dev/null 2>&1 && echo 'available' || echo 'not-available'"],
|
||||
$serverToCheck
|
||||
);
|
||||
|
||||
if (trim($buildkitEnabled) !== 'available') {
|
||||
$buildkitTest = instant_remote_process(
|
||||
["DOCKER_BUILDKIT=1 docker build --help 2>&1 | grep -q 'secret' && echo 'supported' || echo 'not-supported'"],
|
||||
$serverToCheck
|
||||
);
|
||||
|
||||
if (trim($buildkitTest) === 'supported') {
|
||||
$this->dockerBuildkitSupported = true;
|
||||
$this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} with BuildKit secrets support detected on {$serverName}.");
|
||||
$this->application_deployment_queue->addLogEntry('Build secrets are enabled and will be used for enhanced security.');
|
||||
} else {
|
||||
$this->dockerBuildkitSupported = false;
|
||||
$this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} on {$serverName} does not have BuildKit secrets support.");
|
||||
$this->application_deployment_queue->addLogEntry('Build secrets feature is enabled but not supported. Using traditional build arguments.');
|
||||
}
|
||||
} else {
|
||||
// Buildx is available, which means BuildKit is available
|
||||
// Now specifically test for secrets support
|
||||
$secretsTest = instant_remote_process(
|
||||
["docker build --help 2>&1 | grep -q 'secret' && echo 'supported' || echo 'not-supported'"],
|
||||
$serverToCheck
|
||||
);
|
||||
|
||||
if (trim($secretsTest) === 'supported') {
|
||||
$this->dockerBuildkitSupported = true;
|
||||
$this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} with BuildKit and Buildx detected on {$serverName}.");
|
||||
$this->application_deployment_queue->addLogEntry('Build secrets are enabled and will be used for enhanced security.');
|
||||
} else {
|
||||
$this->dockerBuildkitSupported = false;
|
||||
$this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} with Buildx on {$serverName}, but secrets not supported.");
|
||||
$this->application_deployment_queue->addLogEntry('Build secrets feature is enabled but not supported. Using traditional build arguments.');
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->dockerBuildkitSupported = false;
|
||||
$this->application_deployment_queue->addLogEntry("Could not detect BuildKit capabilities on {$serverName}: {$e->getMessage()}");
|
||||
$this->application_deployment_queue->addLogEntry('Build secrets feature is enabled but detection failed. Using traditional build arguments.');
|
||||
}
|
||||
}
|
||||
|
||||
private function decide_what_to_do()
|
||||
{
|
||||
if ($this->restart_only) {
|
||||
|
|
@ -471,11 +559,20 @@ private function deploy_docker_compose_buildpack()
|
|||
}
|
||||
$this->generate_image_names();
|
||||
$this->cleanup_git();
|
||||
|
||||
$this->generate_build_env_variables();
|
||||
|
||||
$this->application->loadComposeFile(isInit: false);
|
||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||
$this->application->oldRawParser();
|
||||
$yaml = $composeFile = $this->application->docker_compose_raw;
|
||||
$this->save_environment_variables();
|
||||
|
||||
// For raw compose, we cannot automatically add secrets configuration
|
||||
// User must define it manually in their docker-compose file
|
||||
if ($this->application->settings->use_build_secrets && $this->dockerBuildkitSupported && ! empty($this->build_secrets)) {
|
||||
$this->application_deployment_queue->addLogEntry('Build secrets are configured. Ensure your docker-compose file includes build.secrets configuration for services that need them.');
|
||||
}
|
||||
} else {
|
||||
$composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id'));
|
||||
$this->save_environment_variables();
|
||||
|
|
@ -494,6 +591,12 @@ private function deploy_docker_compose_buildpack()
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
// Add build secrets to compose file if enabled and BuildKit is supported
|
||||
if ($this->application->settings->use_build_secrets && $this->dockerBuildkitSupported && ! empty($this->build_secrets)) {
|
||||
$composeFile = $this->add_build_secrets_to_compose($composeFile);
|
||||
}
|
||||
|
||||
$yaml = Yaml::dump(convertToArray($composeFile), 10);
|
||||
}
|
||||
$this->docker_compose_base64 = base64_encode($yaml);
|
||||
|
|
@ -505,11 +608,20 @@ private function deploy_docker_compose_buildpack()
|
|||
$this->application_deployment_queue->addLogEntry('Pulling & building required images.');
|
||||
|
||||
if ($this->docker_compose_custom_build_command) {
|
||||
// Prepend DOCKER_BUILDKIT=1 if BuildKit is supported
|
||||
$build_command = $this->docker_compose_custom_build_command;
|
||||
if ($this->dockerBuildkitSupported) {
|
||||
$build_command = "DOCKER_BUILDKIT=1 {$build_command}";
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_build_command}"), 'hidden' => true],
|
||||
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$build_command}"), 'hidden' => true],
|
||||
);
|
||||
} else {
|
||||
$command = "{$this->coolify_variables} docker compose";
|
||||
// Prepend DOCKER_BUILDKIT=1 if BuildKit is supported
|
||||
if ($this->dockerBuildkitSupported) {
|
||||
$command = "DOCKER_BUILDKIT=1 {$command}";
|
||||
}
|
||||
if (filled($this->env_filename)) {
|
||||
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
|
||||
}
|
||||
|
|
@ -669,7 +781,7 @@ private function deploy_static_buildpack()
|
|||
$this->clone_repository();
|
||||
$this->cleanup_git();
|
||||
$this->generate_compose_file();
|
||||
$this->build_image();
|
||||
$this->build_static_image();
|
||||
$this->push_to_docker_registry();
|
||||
$this->rolling_update();
|
||||
}
|
||||
|
|
@ -1146,6 +1258,7 @@ private function laravel_finetunes()
|
|||
|
||||
private function rolling_update()
|
||||
{
|
||||
$this->checkForCancellation();
|
||||
if ($this->server->isSwarm()) {
|
||||
$this->application_deployment_queue->addLogEntry('Rolling update started.');
|
||||
$this->execute_remote_command(
|
||||
|
|
@ -1306,7 +1419,6 @@ private function deploy_pull_request()
|
|||
}
|
||||
$this->build_image();
|
||||
$this->push_to_docker_registry();
|
||||
// $this->stop_running_container();
|
||||
$this->rolling_update();
|
||||
}
|
||||
|
||||
|
|
@ -1342,22 +1454,26 @@ private function create_workdir()
|
|||
|
||||
private function prepare_builder_image()
|
||||
{
|
||||
$this->checkForCancellation();
|
||||
$settings = instanceSettings();
|
||||
$helperImage = config('constants.coolify.helper_image');
|
||||
$helperImage = "{$helperImage}:{$settings->helper_version}";
|
||||
// Get user home directory
|
||||
$this->serverUserHomeDir = instant_remote_process(['echo $HOME'], $this->server);
|
||||
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
|
||||
|
||||
$env_flags = $this->generate_docker_env_flags_for_secrets();
|
||||
|
||||
if ($this->use_build_server) {
|
||||
if ($this->dockerConfigFileExists === 'NOK') {
|
||||
throw new RuntimeException('Docker config file (~/.docker/config.json) not found on the build server. Please run "docker login" to login to the docker registry on the server.');
|
||||
}
|
||||
$runCommand = "docker run -d --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
$runCommand = "docker run -d --name {$this->deployment_uuid} {$env_flags} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
} else {
|
||||
if ($this->dockerConfigFileExists === 'OK') {
|
||||
$runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
$runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} {$env_flags} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
} else {
|
||||
$runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
$runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} {$env_flags} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
}
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
|
||||
|
|
@ -1813,6 +1929,7 @@ private function generate_env_variables()
|
|||
|
||||
private function generate_compose_file()
|
||||
{
|
||||
$this->checkForCancellation();
|
||||
$this->create_workdir();
|
||||
$ports = $this->application->main_port();
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
|
|
@ -2125,16 +2242,74 @@ private function pull_latest_image($image)
|
|||
);
|
||||
}
|
||||
|
||||
private function build_static_image()
|
||||
{
|
||||
$this->application_deployment_queue->addLogEntry('----------------------------------------');
|
||||
$this->application_deployment_queue->addLogEntry('Static deployment. Copying static assets to the image.');
|
||||
if ($this->application->static_image) {
|
||||
$this->pull_latest_image($this->application->static_image);
|
||||
}
|
||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
LABEL coolify.deploymentId={$this->deployment_uuid}
|
||||
COPY . .
|
||||
RUN rm -f /usr/share/nginx/html/nginx.conf
|
||||
RUN rm -f /usr/share/nginx/html/Dockerfile
|
||||
RUN rm -f /usr/share/nginx/html/docker-compose.yaml
|
||||
RUN rm -f /usr/share/nginx/html/.env
|
||||
COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
|
||||
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
|
||||
} else {
|
||||
if ($this->application->settings->is_spa) {
|
||||
$nginx_config = base64_encode(defaultNginxConfiguration('spa'));
|
||||
} else {
|
||||
$nginx_config = base64_encode(defaultNginxConfiguration());
|
||||
}
|
||||
}
|
||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null"),
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null"),
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
$this->application_deployment_queue->addLogEntry('Building docker image completed.');
|
||||
}
|
||||
|
||||
private function build_image()
|
||||
{
|
||||
// Add Coolify related variables to the build args
|
||||
$this->environment_variables->filter(function ($key, $value) {
|
||||
return str($key)->startsWith('COOLIFY_');
|
||||
})->each(function ($key, $value) {
|
||||
$this->build_args->push("--build-arg '{$key}'");
|
||||
});
|
||||
// Add Coolify related variables to the build args/secrets
|
||||
if ($this->dockerBuildkitSupported) {
|
||||
// Coolify variables are already included in the secrets from generate_build_env_variables
|
||||
// build_secrets is already a string at this point
|
||||
} else {
|
||||
// Traditional build args approach
|
||||
$this->environment_variables->filter(function ($key, $value) {
|
||||
return str($key)->startsWith('COOLIFY_');
|
||||
})->each(function ($key, $value) {
|
||||
$this->build_args->push("--build-arg '{$key}'");
|
||||
});
|
||||
|
||||
$this->build_args = $this->build_args->implode(' ');
|
||||
$this->build_args = $this->build_args instanceof \Illuminate\Support\Collection
|
||||
? $this->build_args->implode(' ')
|
||||
: (string) $this->build_args;
|
||||
}
|
||||
|
||||
$this->application_deployment_queue->addLogEntry('----------------------------------------');
|
||||
if ($this->disableBuildCache) {
|
||||
|
|
@ -2147,106 +2322,114 @@ private function build_image()
|
|||
$this->application_deployment_queue->addLogEntry('To check the current progress, click on Show Debug Logs.');
|
||||
}
|
||||
|
||||
if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
|
||||
if ($this->application->settings->is_static) {
|
||||
if ($this->application->static_image) {
|
||||
$this->pull_latest_image($this->application->static_image);
|
||||
$this->application_deployment_queue->addLogEntry('Continuing with the building process.');
|
||||
}
|
||||
if ($this->application->build_pack === 'static') {
|
||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
LABEL coolify.deploymentId={$this->deployment_uuid}
|
||||
COPY . .
|
||||
RUN rm -f /usr/share/nginx/html/nginx.conf
|
||||
RUN rm -f /usr/share/nginx/html/Dockerfile
|
||||
RUN rm -f /usr/share/nginx/html/docker-compose.yaml
|
||||
RUN rm -f /usr/share/nginx/html/.env
|
||||
COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
|
||||
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
|
||||
} else {
|
||||
if ($this->application->settings->is_spa) {
|
||||
$nginx_config = base64_encode(defaultNginxConfiguration('spa'));
|
||||
if ($this->application->build_pack === 'nixpacks') {
|
||||
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
], [
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) {
|
||||
// Modify the nixpacks Dockerfile to use build secrets
|
||||
$this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile");
|
||||
$secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : '';
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
||||
} elseif ($this->dockerBuildkitSupported) {
|
||||
// BuildKit without secrets
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->build_image_name} {$this->build_args} {$this->workdir}";
|
||||
} else {
|
||||
$nginx_config = base64_encode(defaultNginxConfiguration());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($this->application->build_pack === 'nixpacks') {
|
||||
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
], [
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->build_image_name} {$this->build_args} {$this->workdir}";
|
||||
}
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
], [
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
if ($this->dockerBuildkitSupported) {
|
||||
// Modify the nixpacks Dockerfile to use build secrets
|
||||
$this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile");
|
||||
$secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : '';
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
], [
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->build_image_name} {$this->build_args} {$this->workdir}";
|
||||
}
|
||||
}
|
||||
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||
} else {
|
||||
// Dockerfile buildpack
|
||||
if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) {
|
||||
// Modify the Dockerfile to use build secrets
|
||||
$this->modify_dockerfile_for_secrets("{$this->workdir}{$this->dockerfile_location}");
|
||||
$secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : '';
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t $this->build_image_name {$this->workdir}";
|
||||
} else {
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t $this->build_image_name {$this->workdir}";
|
||||
}
|
||||
} else {
|
||||
// Traditional build with args
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}";
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
} else {
|
||||
$build_command = "docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}";
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
LABEL coolify.deploymentId={$this->deployment_uuid}
|
||||
COPY --from=$this->build_image_name /app/{$this->application->publish_directory} .
|
||||
COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
|
||||
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
|
||||
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
|
||||
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
|
||||
} else {
|
||||
if ($this->application->settings->is_spa) {
|
||||
$nginx_config = base64_encode(defaultNginxConfiguration('spa'));
|
||||
} else {
|
||||
if ($this->application->settings->is_spa) {
|
||||
$nginx_config = base64_encode(defaultNginxConfiguration('spa'));
|
||||
} else {
|
||||
$nginx_config = base64_encode(defaultNginxConfiguration());
|
||||
}
|
||||
$nginx_config = base64_encode(defaultNginxConfiguration());
|
||||
}
|
||||
}
|
||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
|
|
@ -2274,10 +2457,22 @@ private function build_image()
|
|||
} else {
|
||||
// Pure Dockerfile based deployment
|
||||
if ($this->application->dockerfile) {
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) {
|
||||
// Modify the Dockerfile to use build secrets
|
||||
$this->modify_dockerfile_for_secrets("{$this->workdir}{$this->dockerfile_location}");
|
||||
$secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : '';
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
}
|
||||
} else {
|
||||
$build_command = "docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
// Traditional build with args
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$build_command = "docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
}
|
||||
}
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
|
|
@ -2306,7 +2501,14 @@ private function build_image()
|
|||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}";
|
||||
if ($this->dockerBuildkitSupported) {
|
||||
// Modify the nixpacks Dockerfile to use build secrets
|
||||
$this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile");
|
||||
$secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : '';
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}";
|
||||
}
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
|
|
@ -2315,7 +2517,14 @@ private function build_image()
|
|||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}";
|
||||
if ($this->dockerBuildkitSupported) {
|
||||
// Modify the nixpacks Dockerfile to use build secrets
|
||||
$this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile");
|
||||
$secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : '';
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}";
|
||||
}
|
||||
}
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
|
|
@ -2334,13 +2543,24 @@ private function build_image()
|
|||
);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||
} else {
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
// Dockerfile buildpack
|
||||
if ($this->dockerBuildkitSupported) {
|
||||
// Use BuildKit with secrets
|
||||
$secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : '';
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$build_command = "DOCKER_BUILDKIT=1 docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
}
|
||||
} else {
|
||||
$build_command = "docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
// Traditional build with args
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$build_command = "docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
}
|
||||
}
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
|
|
@ -2436,49 +2656,207 @@ private function generate_build_env_variables()
|
|||
$variables = collect([])->merge($this->env_args);
|
||||
}
|
||||
|
||||
$this->build_args = $variables->map(function ($value, $key) {
|
||||
$value = escapeshellarg($value);
|
||||
// Check if build secrets are enabled and BuildKit is supported
|
||||
if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) {
|
||||
$this->generate_build_secrets($variables);
|
||||
$this->build_args = '';
|
||||
} else {
|
||||
// Fall back to traditional build args
|
||||
$this->build_args = $variables->map(function ($value, $key) {
|
||||
$value = escapeshellarg($value);
|
||||
|
||||
return "--build-arg {$key}={$value}";
|
||||
});
|
||||
return "--build-arg {$key}={$value}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private function generate_docker_env_flags_for_secrets()
|
||||
{
|
||||
// Only generate env flags if build secrets are enabled
|
||||
if (! $this->application->settings->use_build_secrets) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$variables = $this->pull_request_id === 0
|
||||
? $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->get()
|
||||
: $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->get();
|
||||
|
||||
if ($variables->isEmpty()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $variables
|
||||
->map(function ($env) {
|
||||
$escaped_value = escapeshellarg($env->real_value);
|
||||
|
||||
return "-e {$env->key}={$escaped_value}";
|
||||
})
|
||||
->implode(' ');
|
||||
}
|
||||
|
||||
private function generate_build_secrets(Collection $variables)
|
||||
{
|
||||
if ($variables->isEmpty()) {
|
||||
$this->build_secrets = '';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->build_secrets = $variables
|
||||
->map(function ($value, $key) {
|
||||
return "--secret id={$key},env={$key}";
|
||||
})
|
||||
->implode(' ');
|
||||
}
|
||||
|
||||
private function add_build_env_variables_to_dockerfile()
|
||||
{
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"),
|
||||
'hidden' => true,
|
||||
'save' => 'dockerfile',
|
||||
]);
|
||||
$dockerfile = collect(str($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
||||
if ($this->dockerBuildkitSupported) {
|
||||
// We dont need to add build secrets to dockerfile for buildkit, as we already added them with --secret flag in function generate_docker_env_flags_for_secrets
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"),
|
||||
'hidden' => true,
|
||||
'save' => 'dockerfile',
|
||||
]);
|
||||
$dockerfile = collect(str($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
||||
|
||||
// Include ALL environment variables as build args (deprecating is_build_time flag)
|
||||
if ($this->pull_request_id === 0) {
|
||||
// Get all environment variables except NIXPACKS_ prefixed ones
|
||||
$envs = $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->get();
|
||||
foreach ($envs as $env) {
|
||||
if (data_get($env, 'is_multiline') === true) {
|
||||
$dockerfile->splice(1, 0, ["ARG {$env->key}"]);
|
||||
} else {
|
||||
$dockerfile->splice(1, 0, ["ARG {$env->key}={$env->real_value}"]);
|
||||
if ($this->pull_request_id === 0) {
|
||||
$envs = $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->get();
|
||||
foreach ($envs as $env) {
|
||||
if (data_get($env, 'is_multiline') === true) {
|
||||
$dockerfile->splice(1, 0, ["ARG {$env->key}"]);
|
||||
} else {
|
||||
$dockerfile->splice(1, 0, ["ARG {$env->key}={$env->real_value}"]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Get all preview environment variables except NIXPACKS_ prefixed ones
|
||||
$envs = $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->get();
|
||||
foreach ($envs as $env) {
|
||||
if (data_get($env, 'is_multiline') === true) {
|
||||
$dockerfile->splice(1, 0, ["ARG {$env->key}"]);
|
||||
} else {
|
||||
$dockerfile->splice(1, 0, ["ARG {$env->key}={$env->real_value}"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Get all preview environment variables except NIXPACKS_ prefixed ones
|
||||
$envs = $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->get();
|
||||
foreach ($envs as $env) {
|
||||
if (data_get($env, 'is_multiline') === true) {
|
||||
$dockerfile->splice(1, 0, ["ARG {$env->key}"]);
|
||||
} else {
|
||||
$dockerfile->splice(1, 0, ["ARG {$env->key}={$env->real_value}"]);
|
||||
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function modify_dockerfile_for_secrets($dockerfile_path)
|
||||
{
|
||||
// Only process if build secrets are enabled and we have secrets to mount
|
||||
if (! $this->application->settings->use_build_secrets || empty($this->build_secrets)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the Dockerfile
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "cat {$dockerfile_path}"),
|
||||
'hidden' => true,
|
||||
'save' => 'dockerfile_content',
|
||||
]);
|
||||
|
||||
$dockerfile = str($this->saved_outputs->get('dockerfile_content'))->trim()->explode("\n");
|
||||
|
||||
// Add BuildKit syntax directive if not present
|
||||
if (! str_starts_with($dockerfile->first(), '# syntax=')) {
|
||||
$dockerfile->prepend('# syntax=docker/dockerfile:1');
|
||||
}
|
||||
|
||||
// Get environment variables for secrets
|
||||
$variables = $this->pull_request_id === 0
|
||||
? $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->get()
|
||||
: $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->get();
|
||||
|
||||
if ($variables->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate mount strings for all secrets
|
||||
$mountStrings = $variables->map(fn ($env) => "--mount=type=secret,id={$env->key},env={$env->key}")->implode(' ');
|
||||
|
||||
$modified = false;
|
||||
$dockerfile = $dockerfile->map(function ($line) use ($mountStrings, &$modified) {
|
||||
$trimmed = ltrim($line);
|
||||
|
||||
// Skip lines that already have secret mounts or are not RUN commands
|
||||
if (str_contains($line, '--mount=type=secret') || ! str_starts_with($trimmed, 'RUN')) {
|
||||
return $line;
|
||||
}
|
||||
|
||||
// Add mount strings to RUN command
|
||||
$originalCommand = trim(substr($trimmed, 3));
|
||||
$modified = true;
|
||||
|
||||
return "RUN {$mountStrings} {$originalCommand}";
|
||||
});
|
||||
|
||||
if ($modified) {
|
||||
// Write the modified Dockerfile back
|
||||
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$dockerfile_path} > /dev/null"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
|
||||
$this->application_deployment_queue->addLogEntry('Modified Dockerfile to use build secrets.');
|
||||
}
|
||||
}
|
||||
|
||||
private function add_build_secrets_to_compose($composeFile)
|
||||
{
|
||||
// Get environment variables for secrets
|
||||
$variables = $this->pull_request_id === 0
|
||||
? $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->get()
|
||||
: $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->get();
|
||||
|
||||
if ($variables->isEmpty()) {
|
||||
return $composeFile;
|
||||
}
|
||||
|
||||
$secrets = [];
|
||||
foreach ($variables as $env) {
|
||||
$secrets[$env->key] = [
|
||||
'environment' => $env->key,
|
||||
];
|
||||
}
|
||||
|
||||
$services = data_get($composeFile, 'services', []);
|
||||
foreach ($services as $serviceName => &$service) {
|
||||
if (isset($service['build'])) {
|
||||
if (is_string($service['build'])) {
|
||||
$service['build'] = [
|
||||
'context' => $service['build'],
|
||||
];
|
||||
}
|
||||
if (! isset($service['build']['secrets'])) {
|
||||
$service['build']['secrets'] = [];
|
||||
}
|
||||
foreach ($variables as $env) {
|
||||
if (! in_array($env->key, $service['build']['secrets'])) {
|
||||
$service['build']['secrets'][] = $env->key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
|
||||
$composeFile['services'] = $services;
|
||||
$existingSecrets = data_get($composeFile, 'secrets', []);
|
||||
if ($existingSecrets instanceof \Illuminate\Support\Collection) {
|
||||
$existingSecrets = $existingSecrets->toArray();
|
||||
}
|
||||
$composeFile['secrets'] = array_replace($existingSecrets, $secrets);
|
||||
|
||||
$this->application_deployment_queue->addLogEntry('Added build secrets configuration to docker-compose file (using environment variables).');
|
||||
|
||||
return $composeFile;
|
||||
}
|
||||
|
||||
private function run_pre_deployment_command()
|
||||
|
|
@ -2546,8 +2924,23 @@ private function run_post_deployment_command()
|
|||
throw new RuntimeException('Post-deployment command: Could not find a valid container. Is the container name correct?');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the deployment was cancelled and abort if it was
|
||||
*/
|
||||
private function checkForCancellation(): void
|
||||
{
|
||||
$this->application_deployment_queue->refresh();
|
||||
if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
|
||||
$this->application_deployment_queue->addLogEntry('Deployment cancelled by user, stopping execution.');
|
||||
throw new \RuntimeException('Deployment cancelled by user', 69420);
|
||||
}
|
||||
}
|
||||
|
||||
private function next(string $status)
|
||||
{
|
||||
// Refresh to get latest status
|
||||
$this->application_deployment_queue->refresh();
|
||||
|
||||
// Never allow changing status from FAILED or CANCELLED_BY_USER to anything else
|
||||
if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::FAILED->value) {
|
||||
$this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
|
||||
|
|
@ -2555,7 +2948,9 @@ private function next(string $status)
|
|||
return;
|
||||
}
|
||||
if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
|
||||
return;
|
||||
// Job was cancelled, stop execution
|
||||
$this->application_deployment_queue->addLogEntry('Deployment cancelled by user, stopping execution.');
|
||||
throw new \RuntimeException('Deployment cancelled by user', 69420);
|
||||
}
|
||||
|
||||
$this->application_deployment_queue->update([
|
||||
|
|
@ -2584,8 +2979,8 @@ public function failed(Throwable $exception): void
|
|||
$code = $exception->getCode();
|
||||
if ($code !== 69420) {
|
||||
// 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
|
||||
if ($this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty()) {
|
||||
// do not remove already running container
|
||||
if ($this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty() || $this->pull_request_id !== 0) {
|
||||
// do not remove already running container for PR deployments
|
||||
} else {
|
||||
$this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr');
|
||||
$this->execute_remote_command(
|
||||
|
|
|
|||
|
|
@ -52,15 +52,24 @@ public function force_start()
|
|||
|
||||
public function cancel()
|
||||
{
|
||||
$kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}";
|
||||
$deployment_uuid = $this->application_deployment_queue->deployment_uuid;
|
||||
$kill_command = "docker rm -f {$deployment_uuid}";
|
||||
$build_server_id = $this->application_deployment_queue->build_server_id ?? $this->application->destination->server_id;
|
||||
$server_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id;
|
||||
|
||||
// First, mark the deployment as cancelled to prevent further processing
|
||||
$this->application_deployment_queue->update([
|
||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||
]);
|
||||
|
||||
try {
|
||||
if ($this->application->settings->is_build_server_enabled) {
|
||||
$server = Server::ownedByCurrentTeam()->find($build_server_id);
|
||||
} else {
|
||||
$server = Server::ownedByCurrentTeam()->find($server_id);
|
||||
}
|
||||
|
||||
// Add cancellation log entry
|
||||
if ($this->application_deployment_queue->logs) {
|
||||
$previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR);
|
||||
|
||||
|
|
@ -77,13 +86,35 @@ public function cancel()
|
|||
'logs' => json_encode($previous_logs, flags: JSON_THROW_ON_ERROR),
|
||||
]);
|
||||
}
|
||||
instant_remote_process([$kill_command], $server);
|
||||
|
||||
// Try to stop the helper container if it exists
|
||||
// Check if container exists first
|
||||
$checkCommand = "docker ps -a --filter name={$deployment_uuid} --format '{{.Names}}'";
|
||||
$containerExists = instant_remote_process([$checkCommand], $server);
|
||||
|
||||
if ($containerExists && str($containerExists)->trim()->isNotEmpty()) {
|
||||
// Container exists, kill it
|
||||
instant_remote_process([$kill_command], $server);
|
||||
} else {
|
||||
// Container hasn't started yet
|
||||
$this->application_deployment_queue->addLogEntry('Helper container not yet started. Deployment will be cancelled when job checks status.');
|
||||
}
|
||||
|
||||
// Also try to kill any running process if we have a process ID
|
||||
if ($this->application_deployment_queue->current_process_id) {
|
||||
try {
|
||||
$processKillCommand = "kill -9 {$this->application_deployment_queue->current_process_id}";
|
||||
instant_remote_process([$processKillCommand], $server);
|
||||
} catch (\Throwable $e) {
|
||||
// Process might already be gone, that's ok
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Still mark as cancelled even if cleanup fails
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->application_deployment_queue->update([
|
||||
'current_process_id' => null,
|
||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||
]);
|
||||
next_after_cancel($server);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public function clone(string $type)
|
|||
$databases = $this->environment->databases();
|
||||
$services = $this->environment->services;
|
||||
foreach ($applications as $application) {
|
||||
$selectedDestination = $this->servers->flatMap(fn ($server) => $server->destinations)->where('id', $this->selectedDestination)->first();
|
||||
$selectedDestination = $this->servers->flatMap(fn ($server) => $server->destinations())->where('id', $this->selectedDestination)->first();
|
||||
clone_application($application, $selectedDestination, [
|
||||
'environment_id' => $environment->id,
|
||||
], $this->cloneVolumeData);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ class All extends Component
|
|||
|
||||
public bool $is_env_sorting_enabled = false;
|
||||
|
||||
public bool $use_build_secrets = false;
|
||||
|
||||
protected $listeners = [
|
||||
'saveKey' => 'submit',
|
||||
'refreshEnvs',
|
||||
|
|
@ -34,6 +36,7 @@ class All extends Component
|
|||
public function mount()
|
||||
{
|
||||
$this->is_env_sorting_enabled = data_get($this->resource, 'settings.is_env_sorting_enabled', false);
|
||||
$this->use_build_secrets = data_get($this->resource, 'settings.use_build_secrets', false);
|
||||
$this->resourceClass = get_class($this->resource);
|
||||
$resourceWithPreviews = [\App\Models\Application::class];
|
||||
$simpleDockerfile = filled(data_get($this->resource, 'dockerfile'));
|
||||
|
|
@ -49,6 +52,7 @@ public function instantSave()
|
|||
$this->authorize('manageEnvironment', $this->resource);
|
||||
|
||||
$this->resource->settings->is_env_sorting_enabled = $this->is_env_sorting_enabled;
|
||||
$this->resource->settings->use_build_secrets = $this->use_build_secrets;
|
||||
$this->resource->settings->save();
|
||||
$this->getDevView();
|
||||
$this->dispatch('success', 'Environment variable settings updated.');
|
||||
|
|
|
|||
|
|
@ -46,6 +46,14 @@ public function execute_remote_command(...$commands)
|
|||
}
|
||||
}
|
||||
|
||||
// Check for cancellation before executing commands
|
||||
if (isset($this->application_deployment_queue)) {
|
||||
$this->application_deployment_queue->refresh();
|
||||
if ($this->application_deployment_queue->status === \App\Enums\ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
|
||||
throw new \RuntimeException('Deployment cancelled by user', 69420);
|
||||
}
|
||||
}
|
||||
|
||||
$maxRetries = config('constants.ssh.max_retries');
|
||||
$attempt = 0;
|
||||
$lastError = null;
|
||||
|
|
@ -73,6 +81,12 @@ public function execute_remote_command(...$commands)
|
|||
// Add log entry for the retry
|
||||
if (isset($this->application_deployment_queue)) {
|
||||
$this->addRetryLogEntry($attempt, $maxRetries, $delay, $errorMessage);
|
||||
|
||||
// Check for cancellation during retry wait
|
||||
$this->application_deployment_queue->refresh();
|
||||
if ($this->application_deployment_queue->status === \App\Enums\ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
|
||||
throw new \RuntimeException('Deployment cancelled by user during retry', 69420);
|
||||
}
|
||||
}
|
||||
|
||||
sleep($delay);
|
||||
|
|
@ -85,6 +99,11 @@ public function execute_remote_command(...$commands)
|
|||
|
||||
// If we exhausted all retries and still failed
|
||||
if (! $commandExecuted && $lastError) {
|
||||
// Now we can set the status to FAILED since all retries have been exhausted
|
||||
if (isset($this->application_deployment_queue)) {
|
||||
$this->application_deployment_queue->status = ApplicationDeploymentStatus::FAILED->value;
|
||||
$this->application_deployment_queue->save();
|
||||
}
|
||||
throw $lastError;
|
||||
}
|
||||
});
|
||||
|
|
@ -160,8 +179,8 @@ private function executeCommandWithProcess($command, $hidden, $customType, $appe
|
|||
$process_result = $process->wait();
|
||||
if ($process_result->exitCode() !== 0) {
|
||||
if (! $ignore_errors) {
|
||||
$this->application_deployment_queue->status = ApplicationDeploymentStatus::FAILED->value;
|
||||
$this->application_deployment_queue->save();
|
||||
// Don't immediately set to FAILED - let the retry logic handle it
|
||||
// This prevents premature status changes during retryable SSH errors
|
||||
throw new \RuntimeException($process_result->errorOutput());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1093,11 +1093,11 @@ function getContainerLogs(Server $server, string $container_id, int $lines = 100
|
|||
{
|
||||
if ($server->isSwarm()) {
|
||||
$output = instant_remote_process([
|
||||
"docker service logs -n {$lines} {$container_id}",
|
||||
"docker service logs -n {$lines} {$container_id} 2>&1",
|
||||
], $server);
|
||||
} else {
|
||||
$output = instant_remote_process([
|
||||
"docker logs -n {$lines} {$container_id}",
|
||||
"docker logs -n {$lines} {$container_id} 2>&1",
|
||||
], $server);
|
||||
}
|
||||
|
||||
|
|
@ -1105,7 +1105,6 @@ function getContainerLogs(Server $server, string $container_id, int $lines = 100
|
|||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function escapeEnvVariables($value)
|
||||
{
|
||||
$search = ['\\', "\r", "\t", "\x0", '"', "'"];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
return [
|
||||
'coolify' => [
|
||||
'version' => '4.0.0-beta.428',
|
||||
'version' => '4.0.0-beta.429',
|
||||
'helper_version' => '1.0.11',
|
||||
'realtime_version' => '1.0.10',
|
||||
'self_hosted' => env('SELF_HOSTED', true),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->boolean('use_build_secrets')->default(false)->after('is_build_server_enabled');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('use_build_secrets');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -13,17 +13,32 @@
|
|||
@endcan
|
||||
</div>
|
||||
<div>Environment variables (secrets) for this resource. </div>
|
||||
@if ($resourceClass === 'App\Models\Application' && data_get($resource, 'build_pack') !== 'dockercompose')
|
||||
<div class="w-64 pt-2">
|
||||
@can('manageEnvironment', $resource)
|
||||
<x-forms.checkbox id="is_env_sorting_enabled" label="Sort alphabetically"
|
||||
helper="Turn this off if one environment is dependent on an other. It will be sorted by creation order (like you pasted them or in the order you created them)."
|
||||
instantSave></x-forms.checkbox>
|
||||
@else
|
||||
<x-forms.checkbox id="is_env_sorting_enabled" label="Sort alphabetically"
|
||||
helper="Turn this off if one environment is dependent on an other. It will be sorted by creation order (like you pasted them or in the order you created them)."
|
||||
disabled></x-forms.checkbox>
|
||||
@endcan
|
||||
@if ($resourceClass === 'App\Models\Application')
|
||||
<div class="flex flex-col gap-2 pt-2">
|
||||
@if (data_get($resource, 'build_pack') !== 'dockercompose')
|
||||
<div class="w-64">
|
||||
@can('manageEnvironment', $resource)
|
||||
<x-forms.checkbox id="is_env_sorting_enabled" label="Sort alphabetically"
|
||||
helper="Turn this off if one environment is dependent on an other. It will be sorted by creation order (like you pasted them or in the order you created them)."
|
||||
instantSave></x-forms.checkbox>
|
||||
@else
|
||||
<x-forms.checkbox id="is_env_sorting_enabled" label="Sort alphabetically"
|
||||
helper="Turn this off if one environment is dependent on an other. It will be sorted by creation order (like you pasted them or in the order you created them)."
|
||||
disabled></x-forms.checkbox>
|
||||
@endcan
|
||||
</div>
|
||||
@endif
|
||||
<div class="w-64">
|
||||
@can('manageEnvironment', $resource)
|
||||
<x-forms.checkbox id="use_build_secrets" label="Use Docker Build Secrets"
|
||||
helper="Enable Docker BuildKit secrets for enhanced security during builds. Secrets won't be exposed in the final image. Requires Docker 18.09+ with BuildKit support."
|
||||
instantSave></x-forms.checkbox>
|
||||
@else
|
||||
<x-forms.checkbox id="use_build_secrets" label="Use Docker Build Secrets"
|
||||
helper="Enable Docker BuildKit secrets for enhanced security during builds. Secrets won't be exposed in the final image. Requires Docker 18.09+ with BuildKit support."
|
||||
disabled></x-forms.checkbox>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@if ($resource->type() === 'service' || $resource?->build_pack === 'dockercompose')
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.428"
|
||||
"version": "4.0.0-beta.429"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.429"
|
||||
"version": "4.0.0-beta.430"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.11"
|
||||
|
|
|
|||
Loading…
Reference in a new issue