fix(deployments): preserve whitespace in saved command output

This commit is contained in:
Andras Bacsai
2026-08-22 17:25:55 +02:00
parent 379abb2526
commit 9f6f583851
2 changed files with 69 additions and 17 deletions
+22 -17
View File
@@ -162,17 +162,16 @@ trait ExecuteRemoteCommand
$remote_command = SshMultiplexingHelper::generateSshCommand($this->server, $command);
$process = Process::timeout(config('constants.ssh.command_timeout'))->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $hidden, $customType, $append, $command_hidden, $skip_command_log) {
$output = str($output)->trim();
if ($output->startsWith('╔')) {
$output = "\n".$output;
}
// Sanitize output to ensure valid UTF-8 encoding before JSON encoding
$sanitized_output = sanitize_utf8_text($output);
$log_output = str($sanitized_output)->trim();
if ($log_output->startsWith('╔')) {
$log_output = "\n".$log_output;
}
$new_log_entry = [
'command' => $skip_command_log || $command_hidden ? null : $this->redact_sensitive_info($command),
'output' => $this->redact_sensitive_info($sanitized_output),
'output' => $this->redact_sensitive_info($log_output),
'type' => $customType ?? ($type === 'err' ? 'stderr' : 'stdout'),
'timestamp' => Carbon::now('UTC'),
'hidden' => $hidden,
@@ -206,17 +205,7 @@ trait ExecuteRemoteCommand
$this->application_deployment_queue->save();
if ($this->save) {
if (data_get($this->saved_outputs, $this->save, null) === null) {
$this->saved_outputs->put($this->save, str());
}
if ($append) {
$current_value = $this->saved_outputs->get($this->save);
$this->saved_outputs->put($this->save, str($current_value.str($sanitized_output)->trim()));
} else {
$this->saved_outputs->put($this->save, str($sanitized_output)->trim());
}
}
$this->saveCommandOutput($sanitized_output, $append);
});
$this->application_deployment_queue->update([
'current_process_id' => $process->id(),
@@ -245,6 +234,22 @@ trait ExecuteRemoteCommand
}
}
private function saveCommandOutput(string $output, bool $append): void
{
if (! $this->save) {
return;
}
if ($append) {
$currentValue = $this->saved_outputs->get($this->save, '');
$this->saved_outputs->put($this->save, str($currentValue.$output));
return;
}
$this->saved_outputs->put($this->save, str($output));
}
/**
* Add a log entry for SSH retry attempts
*/
+47
View File
@@ -0,0 +1,47 @@
<?php
use App\Traits\ExecuteRemoteCommand;
use Illuminate\Support\Collection;
require_once __DIR__.'/../../app/Traits/ExecuteRemoteCommand.php';
function remoteCommandOutputCollector(): object
{
return new class
{
use ExecuteRemoteCommand;
public Collection $saved_outputs;
public function __construct()
{
$this->save = 'dockerfile';
$this->saved_outputs = collect();
}
public function collectOutput(string $output, bool $append = true): void
{
$this->saveCommandOutput($output, $append);
}
};
}
it('preserves whitespace across streamed saved output chunks', function () {
$collector = remoteCommandOutputCollector();
foreach (["FROM alpine\nARG FIRST", "\n", 'ARG', ' ', "SECOND\nRUN true\n"] as $chunk) {
$collector->collectOutput($chunk);
}
expect((string) $collector->saved_outputs->get('dockerfile'))
->toBe("FROM alpine\nARG FIRST\nARG SECOND\nRUN true\n");
});
it('replaces saved output without trimming it when append is disabled', function () {
$collector = remoteCommandOutputCollector();
$collector->collectOutput('old');
$collector->collectOutput(" new output\n", append: false);
expect((string) $collector->saved_outputs->get('dockerfile'))->toBe(" new output\n");
});