diff --git a/app/Http/Livewire/Project/Database/Postgresql/General.php b/app/Http/Livewire/Project/Database/Postgresql/General.php index 9045eee69..5a03908e1 100644 --- a/app/Http/Livewire/Project/Database/Postgresql/General.php +++ b/app/Http/Livewire/Project/Database/Postgresql/General.php @@ -50,8 +50,9 @@ class General extends Component $this->getDbUrl(); } public function getDbUrl() { + if ($this->database->is_public) { - $this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->destination->server->ip}:{$this->database->public_port}/{$this->database->postgres_db}"; + $this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->destination->server->getIp}:{$this->database->public_port}/{$this->database->postgres_db}"; } else { $this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->uuid}:5432/{$this->database->postgres_db}"; } diff --git a/app/Http/Livewire/Project/Service/Index.php b/app/Http/Livewire/Project/Service/Index.php index 18029fb73..3f48fdf5d 100644 --- a/app/Http/Livewire/Project/Service/Index.php +++ b/app/Http/Livewire/Project/Service/Index.php @@ -29,7 +29,10 @@ class Index extends Component $this->parameters = get_route_parameters(); $this->query = request()->query(); $this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail(); - $this->refreshStack(); + $this->applications = $this->service->applications->sort(); + $this->databases = $this->service->databases->sort(); + ray($this->applications); + ray($this->databases); } public function saveCompose($raw) { diff --git a/app/Http/Livewire/Project/Service/Storage.php b/app/Http/Livewire/Project/Service/Storage.php new file mode 100644 index 000000000..2e3ef8f58 --- /dev/null +++ b/app/Http/Livewire/Project/Service/Storage.php @@ -0,0 +1,35 @@ + $data['name'], + 'mount_path' => $data['mount_path'], + 'host_path' => $data['host_path'], + 'resource_id' => $this->resource->id, + 'resource_type' => $this->resource->getMorphClass(), + ]); + $this->resource->refresh(); + $this->emit('success', 'Storage added successfully'); + $this->emit('clearAddStorage'); + $this->emit('refreshStorages'); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } +} diff --git a/app/Http/Livewire/Project/Shared/Storages/Add.php b/app/Http/Livewire/Project/Shared/Storages/Add.php index 1edfe03a4..7278eda7a 100644 --- a/app/Http/Livewire/Project/Shared/Storages/Add.php +++ b/app/Http/Livewire/Project/Shared/Storages/Add.php @@ -33,7 +33,7 @@ class Add extends Component { $this->validate(); $name = $this->uuid . '-' . $this->name; - $this->emitUp('submit', [ + $this->emit('addNewVolume', [ 'name' => $name, 'mount_path' => $this->mount_path, 'host_path' => $this->host_path, diff --git a/app/Http/Livewire/Project/Shared/Storages/All.php b/app/Http/Livewire/Project/Shared/Storages/All.php index 8ac22d881..457071711 100644 --- a/app/Http/Livewire/Project/Shared/Storages/All.php +++ b/app/Http/Livewire/Project/Shared/Storages/All.php @@ -7,30 +7,11 @@ use Livewire\Component; class All extends Component { - public bool $isHeaderVisible = true; public $resource; - protected $listeners = ['refreshStorages', 'submit']; + protected $listeners = ['refreshStorages']; public function refreshStorages() { $this->resource->refresh(); } - - public function submit($data) - { - try { - LocalPersistentVolume::create([ - 'name' => $data['name'], - 'mount_path' => $data['mount_path'], - 'host_path' => $data['host_path'], - 'resource_id' => $this->resource->id, - 'resource_type' => $this->resource->getMorphClass(), - ]); - $this->resource->refresh(); - $this->emit('success', 'Storage added successfully'); - $this->emit('clearAddStorage'); - } catch (\Throwable $e) { - return handleError($e, $this); - } - } } diff --git a/app/Models/Application.php b/app/Models/Application.php index fc437769a..af2db98ee 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -44,6 +44,10 @@ class Application extends BaseModel { return $this->morphMany(LocalPersistentVolume::class, 'resource'); } + public function fileStorages() + { + return $this->morphMany(LocalFileVolume::class, 'resource'); + } public function type() { diff --git a/app/Models/LocalFileVolume.php b/app/Models/LocalFileVolume.php index b45a868aa..f426e40a0 100644 --- a/app/Models/LocalFileVolume.php +++ b/app/Models/LocalFileVolume.php @@ -31,23 +31,18 @@ class LocalFileVolume extends BaseModel } $isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server); $isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server); - ray($isFile); if ($isFile == 'OK' && $fileVolume->is_directory) { throw new \Exception("File $path is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory."); } else if ($isDir == 'OK' && !$fileVolume->is_directory) { throw new \Exception("File $path is a directory on the server, but you are trying to mark it as a file. Please delete the directory on the server or mark it as directory."); } - if (($isFile == 'NOK' && !$fileVolume->is_directory) || $isFile == 'OK') { - $rootDir = Str::of($path)->dirname(); - $commands->push("mkdir -p $rootDir > /dev/null 2>&1 || true"); - $commands->push("touch $path > /dev/null 2>&1 || true"); - if ($content) { - $content = base64_encode($content); - $commands->push("echo '$content' | base64 -d > $path"); - } - } else if ($isDir == 'NOK' && $fileVolume->is_directory) { + if (!$fileVolume->is_directory && $isDir == 'NOK') { + $content = base64_encode($content); + $commands->push("echo '$content' | base64 -d > $path"); + } else if ($isDir == 'NOK' && $fileVolume->is_directory) { $commands->push("mkdir -p $path > /dev/null 2>&1 || true"); } + ray($commands->toArray()); return instant_remote_process($commands, $server); } } diff --git a/app/Models/Server.php b/app/Models/Server.php index 0619a589e..f174222c0 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -5,6 +5,7 @@ namespace App\Models; use App\Enums\ProxyStatus; use App\Enums\ProxyTypes; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Spatie\SchemalessAttributes\Casts\SchemalessAttributes; use Spatie\SchemalessAttributes\SchemalessAttributesTrait; @@ -120,7 +121,20 @@ class Server extends BaseModel { return $this->hasMany(Service::class); } - + public function getIp(): Attribute + { + return Attribute::make( + get: function () { + if (isDev()) { + return '127.0.0.1'; + } + if ($this->ip === 'host.docker.internal') { + return base_ip(); + } + return $this->ip; + } + ); + } public function previews() { return $this->destinations()->map(function ($standaloneDocker) { diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php index 057db8e67..b4a130904 100644 --- a/app/Models/ServiceDatabase.php +++ b/app/Models/ServiceDatabase.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Symfony\Component\Yaml\Yaml; class ServiceDatabase extends BaseModel { diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 17b5b8056..ed270862b 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -76,6 +76,11 @@ class StandalonePostgresql extends BaseModel return $this->belongsTo(Environment::class); } + public function fileStorages() + { + return $this->morphMany(LocalFileVolume::class, 'resource'); + } + public function destination() { return $this->morphTo(); diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php index 306ca172f..2590049ad 100644 --- a/bootstrap/helpers/services.php +++ b/bootstrap/helpers/services.php @@ -85,28 +85,39 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneS } else { $fileLocation = $path; } + ray($path,$fileLocation); + // Exists and is a file $isFile = instant_remote_process(["test -f $fileLocation && echo OK || echo NOK"], $server); + // Exists and is a directory $isDir = instant_remote_process(["test -d $fileLocation && echo OK || echo NOK"], $server); - if ($isFile === 'NOK' &&!$fileVolume->is_directory && $isInit) { - $fileVolume->saveStorageOnServer($oneService); - continue; - } - if ($isFile == 'OK' && !$fileVolume->is_directory) { + + if ($isFile == 'OK') { + // If its a file & exists $filesystemContent = instant_remote_process(["cat $fileLocation"], $server); - if (base64_encode($filesystemContent) != base64_encode($content)) { - $fileVolume->content = $filesystemContent; - $fileVolume->save(); - } - } else { - if ($isDir == 'OK') { - $fileVolume->content = null; - $fileVolume->is_directory = true; - $fileVolume->save(); - } else { - $fileVolume->content = null; - $fileVolume->is_directory = false; - $fileVolume->save(); - } + $fileVolume->content = $filesystemContent; + $fileVolume->is_directory = false; + $fileVolume->save(); + } else if ($isDir == 'OK') { + // If its a directory & exists + $fileVolume->content = null; + $fileVolume->is_directory = true; + $fileVolume->save(); + } else if ($isFile == 'NOK' && $isDir == 'NOK' && !$fileVolume->is_directory && $isInit && $content) { + // Does not exists (no dir or file), not flagged as directory, is init, has content + $fileVolume->content = $content; + $fileVolume->is_directory = false; + $fileVolume->save(); + $content = base64_encode($content); + $dir = Str::of($fileLocation)->dirname(); + instant_remote_process([ + "mkdir -p $dir", + "echo '$content' | base64 -d > $fileLocation" + ], $server); + } else if ($isFile == 'NOK' && $isDir == 'NOK' && $fileVolume->is_directory && $isInit) { + $fileVolume->content = null; + $fileVolume->is_directory = true; + $fileVolume->save(); + instant_remote_process(["mkdir -p $fileLocation"], $server); } } } catch (\Throwable $e) { diff --git a/config/sentry.php b/config/sentry.php index 4c571d7c3..3754637d7 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.62', + 'release' => '4.0.0-beta.63', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index a58b7c0ee..51a7a5302 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@
https://hub.docker.com/_/postgres" /> -
- @if ($database->started_at) - - - started_at) +
+ + + - @else - - + helper="You can only change this in the database." /> +
+ @else +
Please verify these values. You can only modify them before the initial start. After that, you need to modify it in the database. +
+
+ + - @endif -
+ placeholder="If empty, it will be the same as Username." /> +
+ @endif
- @@ -55,11 +57,12 @@

Network

- - -
- + helper="A comma separated list of ports you would like to map to the host system.
Example3000:5432,3002:5433" /> + + +
+
diff --git a/resources/views/livewire/project/service/index.blade.php b/resources/views/livewire/project/service/index.blade.php index 28a2deb63..c5f16c727 100644 --- a/resources/views/livewire/project/service/index.blade.php +++ b/resources/views/livewire/project/service/index.blade.php @@ -1,4 +1,4 @@ -
+
@@ -102,42 +102,17 @@
+
+

Storages

+
+
Persistent storage to preserve data between deployments.
+ Please modify storage layout in your Docker Compose file. @foreach ($applications as $application) - @if ($loop->first) - - @else - - @endif - @if ($application->fileStorages()->get()->count() > 0) -
Mounted Files/Dirs (binds)
-
- @foreach ($application->fileStorages()->get()->sort() as $fileStorage) - - @endforeach -
- @endif + @endforeach @foreach ($databases as $database) - @if ($loop->first) -

{{ Str::headline($database->name) }}

- @if ($applications->count() > 0) - - @else - - @endif - @if ($database->fileStorages()->get()->count() > 0) -
Mounted Files/Dirs (binds)
-
- @foreach ($database->fileStorages()->get()->sort() as $fileStorage) - - @endforeach -
- @endif - @else - - @endif + @endforeach
diff --git a/resources/views/livewire/project/service/show.blade.php b/resources/views/livewire/project/service/show.blade.php index b789e6550..0ac516660 100644 --- a/resources/views/livewire/project/service/show.blade.php +++ b/resources/views/livewire/project/service/show.blade.php @@ -24,15 +24,13 @@
- - @if ($serviceApplication->fileStorages()->get()->count() > 0) -
Mounted Files/Dirs (binds)
-
- @foreach ($serviceApplication->fileStorages()->get()->sort() as $fileStorage) - - @endforeach -
- @endif +
+

Storages

+
+
Persistent storage to preserve data between deployments.
+ Please modify storage layout in your Docker Compose file. +
@endisset @isset($serviceDatabase) @@ -40,15 +38,13 @@
- - @if ($serviceDatabase->fileStorages()->get()->count() > 0) -
Mounted Files/Dirs (binds)
-
- @foreach ($serviceDatabase->fileStorages()->get()->sort() as $fileStorage) - - @endforeach -
- @endif +
+

Storages

+
+
Persistent storage to preserve data between deployments.
+ Please modify storage layout in your Docker Compose file. +
@endisset
diff --git a/resources/views/livewire/project/service/storage.blade.php b/resources/views/livewire/project/service/storage.blade.php new file mode 100644 index 000000000..97a089cf8 --- /dev/null +++ b/resources/views/livewire/project/service/storage.blade.php @@ -0,0 +1,49 @@ +
+ @if ( + $resource->getMorphClass() == 'App\Models\Application' || + $resource->getMorphClass() == 'App\Models\StandalonePostgresql') +
+

Storages

+ + + Add + +
+
Persistent storage to preserve data between deployments.
+ @if ( + $resource->persistentStorages()->get()->count() === 0 && + $resource->fileStorages()->get()->count() == 0) +
No storage found.
+ @else + @if ($resource->persistentStorages()->get()->count() > 0) + + @endif + @if ($resource->fileStorages()->get()->count() > 0) +
+ @foreach ($resource->fileStorages()->get()->sort() as $fileStorage) + + @endforeach +
+ @endif + @endif + @else + @if ( + $resource->persistentStorages()->get()->count() > 0 || + $resource->fileStorages()->get()->count() > 0) +

{{ Str::headline($resource->name) }}

+ @endif + @if ($resource->persistentStorages()->get()->count() > 0) + + @endif + @if ($resource->fileStorages()->get()->count() > 0) +
+ @foreach ($resource->fileStorages()->get()->sort() as $fileStorage) + + @endforeach +
+ @endif + @endif +
diff --git a/resources/views/livewire/project/shared/logs.blade.php b/resources/views/livewire/project/shared/logs.blade.php index 2fe2a74cb..e12145190 100644 --- a/resources/views/livewire/project/shared/logs.blade.php +++ b/resources/views/livewire/project/shared/logs.blade.php @@ -29,13 +29,8 @@
- @if (serviceStatus($resource) === 'running') - - @else - Service is not running. - @endif +
@endif - diff --git a/resources/views/livewire/project/shared/storages/all.blade.php b/resources/views/livewire/project/shared/storages/all.blade.php index fbf8cd593..dfd2064dc 100644 --- a/resources/views/livewire/project/shared/storages/all.blade.php +++ b/resources/views/livewire/project/shared/storages/all.blade.php @@ -1,25 +1,4 @@
- @if ($isHeaderVisible) -
-
-

Storages

- @if ($resource->type() !== 'service') - - + Add - - @endif -
-
Persistent storage to preserve data between deployments.
- @if ($resource->type() === 'service') - Please modify storage layout in your Docker Compose file. -

{{ Str::headline($resource->name) }}

- @endif -
- @endif
@foreach ($resource->persistentStorages as $storage) @if ($resource->type() === 'service') diff --git a/resources/views/project/application/configuration.blade.php b/resources/views/project/application/configuration.blade.php index 6d8971c8f..78f0a3061 100644 --- a/resources/views/project/application/configuration.blade.php +++ b/resources/views/project/application/configuration.blade.php @@ -14,8 +14,7 @@ @click.prevent="activeTab = 'source'; window.location.hash = 'source'" href="#">Source @endif Server + @click.prevent="activeTab = 'server'; window.location.hash = 'server'" href="#">Server Storages @@ -27,8 +26,8 @@ @endif Health Checks - + @click.prevent="activeTab = 'health'; window.location.hash = 'health'" href="#">Health Checks + Rollback @@ -56,7 +55,7 @@
- +
diff --git a/resources/views/project/database/configuration.blade.php b/resources/views/project/database/configuration.blade.php index 5c16a0050..78daa3b0c 100644 --- a/resources/views/project/database/configuration.blade.php +++ b/resources/views/project/database/configuration.blade.php @@ -47,7 +47,7 @@
- +
diff --git a/scripts/sync_volume.sh b/scripts/sync_volume.sh new file mode 100644 index 000000000..43631fdf7 --- /dev/null +++ b/scripts/sync_volume.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Sync docker volumes between two servers + +VERSION="1.0.0" +SOURCE=$1 +DESTINATION=$2 +set -e +if [ -z "$SOURCE" ]; then + echo "Source server is not specified." + exit 1 +fi +if [ -z "$DESTINATION" ]; then + echo "Destination server is not specified." + exit 1 +fi + +SOURCE_USER=$(echo $SOURCE | cut -d@ -f1) +SOURCE_SERVER=$(echo $SOURCE | cut -d: -f1 | cut -d@ -f2) +SOURCE_PORT=$(echo $SOURCE | cut -d: -f2 | cut -d/ -f1) +SOURCE_VOLUME_NAME=$(echo $SOURCE | cut -d/ -f2) + +if ! [[ "$SOURCE_PORT" =~ ^[0-9]+$ ]]; then + echo "Invalid source port: $SOURCE_PORT" + exit 1 +fi + +DESTINATION_USER=$(echo $DESTINATION | cut -d@ -f1) +DESTINATION_SERVER=$(echo $DESTINATION | cut -d: -f1 | cut -d@ -f2) +DESTINATION_PORT=$(echo $DESTINATION | cut -d: -f2 | cut -d/ -f1) +DESTINATION_VOLUME_NAME=$(echo $DESTINATION | cut -d/ -f2) + +if ! [[ "$DESTINATION_PORT" =~ ^[0-9]+$ ]]; then + echo "Invalid destination port: $DESTINATION_PORT" + exit 1 +fi + +echo "Generating backup file to ./$SOURCE_VOLUME_NAME.tgz" +ssh -p $SOURCE_PORT $SOURCE_USER@$SOURCE_SERVER "docker run -v $SOURCE_VOLUME_NAME:/volume --rm --log-driver none loomchild/volume-backup backup -c pigz -v" >./$SOURCE_VOLUME_NAME.tgz +echo "" +if [ -f "./$SOURCE_VOLUME_NAME.tgz" ]; then + echo "Uploading backup file to $DESTINATION_SERVER:~/$DESTINATION_VOLUME_NAME.tgz" + scp -P $DESTINATION_PORT ./$SOURCE_VOLUME_NAME.tgz $DESTINATION_USER@$DESTINATION_SERVER:~/$DESTINATION_VOLUME_NAME.tgz + echo "" + echo "Restoring backup file on remote ($DESTINATION_SERVER:/~/$DESTINATION_VOLUME_NAME.tgz)" + ssh -p $DESTINATION_PORT $DESTINATION_USER@$DESTINATION_SERVER "docker run -i -v $DESTINATION_VOLUME_NAME:/volume --log-driver none --rm loomchild/volume-backup restore -c pigz -vf < ~/$DESTINATION_VOLUME_NAME.tgz" + echo "" + echo "Deleting backup file on remote ($DESTINATION_SERVER:/~/$DESTINATION_VOLUME_NAME.tgz)" + ssh -p $DESTINATION_PORT $DESTINATION_USER@$DESTINATION_SERVER "rm ~/$DESTINATION_VOLUME_NAME.tgz" + + echo "" + echo "Local file ./$SOURCE_VOLUME_NAME.tgz is not deleted." + + echo "" + echo "WARNING: If you are copying a database volume, you need to set the right users/passwords on the destination service's environment variables." + echo "Why? Because we are copying the volume as-is, so the database credentials will bethe same as on the source volume." +fi + diff --git a/templates/deprecated.json b/templates/deprecated.json index 595c5ec6f..59c1be455 100644 --- a/templates/deprecated.json +++ b/templates/deprecated.json @@ -2,6 +2,6 @@ "plausible-analytics": { "documentation": "https://plausible.io/docs", "slogan": "A lighweight and open-source website analytics tool.", - "compose": "dmVyc2lvbjogIjMuMyIKc2VydmljZXM6CiAgcGxhdXNpYmxlLWFuYWx5dGljczoKICAgIGltYWdlOiBwbGF1c2libGUvYW5hbHl0aWNzOnYyLjAKICAgIGNvbW1hbmQ6IHNoIC1jICJzbGVlcCAxMCAmJiAvZW50cnlwb2ludC5zaCBkYiBjcmVhdGVkYiAmJiAvZW50cnlwb2ludC5zaCBkYiBtaWdyYXRlICYmIC9lbnRyeXBvaW50LnNoIHJ1biIKICAgIGVudmlyb25tZW50OgogICAgICAtIERBVEFCQVNFX1VSTD1wb3N0Z3JlczovL3Bvc3RncmVzOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBsYXVzaWJsZV9kYi9wbGF1c2libGUKICAgICAgLSBCQVNFX1VSTD0kU0VSVklDRV9GUUROX1BMQVVTSUJMRQogICAgICAtIFNFQ1JFVF9LRVlfQkFTRT0kU0VSVklDRV9CQVNFNjRfNjRfUExBVVNJQkxFCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBsYXVzaWJsZV9kYgogICAgICAtIHBsYXVzaWJsZV9ldmVudHNfZGIKICAgICAgLSBtYWlsCgogIG1haWw6CiAgICBpbWFnZTogYnl0ZW1hcmsvc210cAoKICBwbGF1c2libGVfZGI6CiAgICBpbWFnZTogcG9zdGdyZXM6MTQtYWxwaW5lCiAgICB2b2x1bWVzOgogICAgICAtIGRiLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19EQj1wbGF1c2libGUKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwoKICBwbGF1c2libGVfZXZlbnRzX2RiOgogICAgaW1hZ2U6IGNsaWNraG91c2UvY2xpY2tob3VzZS1zZXJ2ZXI6MjMuMy43LjUtYWxwaW5lCiAgICB2b2x1bWVzOgogICAgICAtIHR5cGU6IHZvbHVtZQogICAgICAgIHNvdXJjZTogZXZlbnQtZGF0YQogICAgICAgIHRhcmdldDogL3Zhci9saWIvY2xpY2tob3VzZQogICAgICAtIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vY2xpY2tob3VzZS9jbGlja2hvdXNlLWNvbmZpZy54bWwKICAgICAgICB0YXJnZXQ6IC9ldGMvY2xpY2tob3VzZS1zZXJ2ZXIvY29uZmlnLmQvbG9nZ2luZy54bWwKICAgICAgICByZWFkX29ubHk6IHRydWUKICAgICAgICBjb250ZW50OiA+LQogICAgICAgICAgPGNsaWNraG91c2U+PHByb2ZpbGVzPjxkZWZhdWx0Pjxsb2dfcXVlcmllcz4wPC9sb2dfcXVlcmllcz48bG9nX3F1ZXJ5X3RocmVhZHM+MDwvbG9nX3F1ZXJ5X3RocmVhZHM+PC9kZWZhdWx0PjwvcHJvZmlsZXM+PC9jbGlja2hvdXNlPgogICAgICAtIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vY2xpY2tob3VzZS9jbGlja2hvdXNlLXVzZXItY29uZmlnLnhtbAogICAgICAgIHRhcmdldDogL2V0Yy9jbGlja2hvdXNlLXNlcnZlci91c2Vycy5kL2xvZ2dpbmcueG1sCiAgICAgICAgcmVhZF9vbmx5OiB0cnVlCiAgICAgICAgY29udGVudDogPi0KICAgICAgICAgIDxjbGlja2hvdXNlPjxsb2dnZXI+PGxldmVsPndhcm5pbmc8L2xldmVsPjxjb25zb2xlPnRydWU8L2NvbnNvbGU+PC9sb2dnZXI+PHF1ZXJ5X3RocmVhZF9sb2cKICAgICAgICAgIHJlbW92ZT0icmVtb3ZlIi8+PHF1ZXJ5X2xvZyByZW1vdmU9InJlbW92ZSIvPjx0ZXh0X2xvZwogICAgICAgICAgcmVtb3ZlPSJyZW1vdmUiLz48dHJhY2VfbG9nIHJlbW92ZT0icmVtb3ZlIi8+PG1ldHJpY19sb2cKICAgICAgICAgIHJlbW92ZT0icmVtb3ZlIi8+PGFzeW5jaHJvbm91c19tZXRyaWNfbG9nCiAgICAgICAgICByZW1vdmU9InJlbW92ZSIvPjxzZXNzaW9uX2xvZyByZW1vdmU9InJlbW92ZSIvPjxwYXJ0X2xvZwogICAgICAgICAgcmVtb3ZlPSJyZW1vdmUiLz48L2NsaWNraG91c2U+CiAgICB1bGltaXRzOgogICAgICBub2ZpbGU6CiAgICAgICAgc29mdDogMjYyMTQ0CiAgICAgICAgaGFyZDogMjYyMTQ0Cg==" + "compose": "dmVyc2lvbjogJzMuMycKc2VydmljZXM6CiAgcGxhdXNpYmxlX2RiOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNC1hbHBpbmUnCiAgICByZXN0YXJ0OiBhbHdheXMKICAgIHZvbHVtZXM6CiAgICAgIC0gJy9ldGMvZGF0YS9wbGF1c2libGUvZGItZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kUE9TVEdSRVNfUEFTU1dPUkQKICBwbGF1c2libGVfZXZlbnRzX2RiOgogICAgaW1hZ2U6ICdjbGlja2hvdXNlL2NsaWNraG91c2Utc2VydmVyOjIzLjMuNy41LWFscGluZScKICAgIHJlc3RhcnQ6IGFsd2F5cwogICAgdm9sdW1lczoKICAgICAgLSAnL2V0Yy9kYXRhL3BsYXVzaWJsZS9ldmVudC1kYXRhOi92YXIvbGliL2NsaWNraG91c2UnCiAgICAgIC0gdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogL2V0Yy9kYXRhL3BsYXVzaWJsZS9jbGlja2hvdXNlL2NsaWNraG91c2UtY29uZmlnLnhtbAogICAgICAgIHRhcmdldDogL2V0Yy9jbGlja2hvdXNlLXNlcnZlci9jb25maWcuZC9sb2dnaW5nLnhtbAogICAgICAgIHJlYWRfb25seTogdHJ1ZQogICAgICAgIGNvbnRlbnQ6ID4tCiAgICAgICAgICA8Y2xpY2tob3VzZT48cHJvZmlsZXM+PGRlZmF1bHQ+PGxvZ19xdWVyaWVzPjA8L2xvZ19xdWVyaWVzPjxsb2dfcXVlcnlfdGhyZWFkcz4wPC9sb2dfcXVlcnlfdGhyZWFkcz48L2RlZmF1bHQ+PC9wcm9maWxlcz48L2NsaWNraG91c2U+CiAgICAgIC0gdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogL2V0Yy9kYXRhL3BsYXVzaWJsZS9jbGlja2hvdXNlL2NsaWNraG91c2UtdXNlci1jb25maWcueG1sCiAgICAgICAgdGFyZ2V0OiAvZXRjL2NsaWNraG91c2Utc2VydmVyL3VzZXJzLmQvbG9nZ2luZy54bWwKICAgICAgICByZWFkX29ubHk6IHRydWUKICAgICAgICBjb250ZW50OiA+LQogICAgICAgICAgPGNsaWNraG91c2U+PGxvZ2dlcj48bGV2ZWw+d2FybmluZzwvbGV2ZWw+PGNvbnNvbGU+dHJ1ZTwvY29uc29sZT48L2xvZ2dlcj48cXVlcnlfdGhyZWFkX2xvZwogICAgICAgICAgcmVtb3ZlPSJyZW1vdmUiLz48cXVlcnlfbG9nIHJlbW92ZT0icmVtb3ZlIi8+PHRleHRfbG9nCiAgICAgICAgICByZW1vdmU9InJlbW92ZSIvPjx0cmFjZV9sb2cgcmVtb3ZlPSJyZW1vdmUiLz48bWV0cmljX2xvZwogICAgICAgICAgcmVtb3ZlPSJyZW1vdmUiLz48YXN5bmNocm9ub3VzX21ldHJpY19sb2cKICAgICAgICAgIHJlbW92ZT0icmVtb3ZlIi8+PHNlc3Npb25fbG9nIHJlbW92ZT0icmVtb3ZlIi8+PHBhcnRfbG9nCiAgICAgICAgICByZW1vdmU9InJlbW92ZSIvPjwvY2xpY2tob3VzZT4KICAgIHVsaW1pdHM6CiAgICAgICAgbm9maWxlOgogICAgICAgICAgc29mdDogMjYyMTQ0CiAgICAgICAgICBoYXJkOiAyNjIxNDQKICBwbGF1c2libGU6CiAgICBpbWFnZTogJ3BsYXVzaWJsZS9hbmFseXRpY3M6djIuMCcKICAgIHJlc3RhcnQ6IGFsd2F5cwogICAgY29tbWFuZDogJ3NoIC1jICJzbGVlcCAxMCAmJiAvZW50cnlwb2ludC5zaCBkYiBjcmVhdGVkYiAmJiAvZW50cnlwb2ludC5zaCBkYiBtaWdyYXRlICYmIC9lbnRyeXBvaW50LnNoIHJ1biInCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBsYXVzaWJsZV9kYgogICAgICAtIHBsYXVzaWJsZV9ldmVudHNfZGIKICAgIHBvcnRzOgogICAgICAtICc4MDAwOjgwMDAnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRUNSRVRfS0VZX0JBU0U9JFNFQ1JFVF9LRVlfQkFTRQogICAgICAtIERBVEFCQVNFX1VSTD0kREFUQUJBU0VfVVJMCiAgICAgIC0gJ0NMSUNLSE9VU0VfREFUQUJBU0VfVVJMPWh0dHA6Ly9wbGF1c2libGVfZXZlbnRzX2RiOjgxMjMvcGxhdXNpYmxlX2V2ZW50c19kYicKICAgICAgLSBNQUlMRVJfQURBUFRFUj0kTUFJTEVSX0FEQVBURVIKICAgICAgLSBTRU5ER1JJRF9BUElfS0VZPSRTRU5ER1JJRF9BUElfS0VZCiAgICAgIC0gR09PR0xFX0NMSUVOVF9JRD0kR09PR0xFX0NMSUVOVF9JRAogICAgICAtIEdPT0dMRV9DTElFTlRfU0VDUkVUPSRHT09HTEVfQ0xJRU5UX1NFQ1JFVAogICAgICAtIERJU0FCTEVfUkVHSVNUUkFUSU9OPSRESVNBQkxFX1JFR0lTVFJBVElPTgogICAgICAtIEJBU0VfVVJMPSRCQVNFX1VSTAogICAgICAtIExPR19GQUlMRURfTE9HSU5fQVRURU1QVFM9JExPR19GQUlMRURfTE9HSU5fQVRURU1QVFMK" }, } diff --git a/versions.json b/versions.json index fcb230af4..8b102d61b 100644 --- a/versions.json +++ b/versions.json @@ -4,7 +4,7 @@ "version": "3.12.36" }, "v4": { - "version": "4.0.0-beta.62" + "version": "4.0.0-beta.63" } } }