From 2a150e9be50ee038c91000a53a2ce71364556f90 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 16:33:11 +0100 Subject: [PATCH 01/11] require statamic 6 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ee0ea55..fdaa186 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "mustangostang/spyc": "dev-master#dfd9aadc1f5224065d55b42b712c7e99a50a3f4d" }, "require-dev": { - "statamic/cms": "^5.41", + "statamic/cms": "^6.0", "mockery/mockery": "^1.4.4", "orchestra/testbench": "^8.28 || ^9.6.1 || ^10.0", "phpunit/phpunit": "^10.5.35 || ^11.0" From 49c73d0891108970b2614822c0e1b020398bfc98 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 16:34:48 +0100 Subject: [PATCH 02/11] drop support for laravel 10 & php 8.2 --- .github/workflows/tests.yml | 15 ++------------- composer.json | 2 +- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5237ef2..b2052fa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,15 +12,11 @@ jobs: strategy: matrix: - php: [8.1, 8.2, 8.3, 8.4] - laravel: [10.*, 11.*, 12.*] + php: [8.3, 8.4] + laravel: [11.*, 12.*] stability: [prefer-lowest, prefer-stable] os: [ubuntu-latest] include: - - os: windows-latest - php: 8.3 - laravel: 10.* - stability: prefer-stable - os: windows-latest php: 8.3 laravel: 11.* @@ -29,13 +25,6 @@ jobs: php: 8.4 laravel: 11.* stability: prefer-stable - exclude: - - php: 8.1 - laravel: 11.* - - php: 8.1 - laravel: 12.* - - php: 8.4 - laravel: 10.* name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} diff --git a/composer.json b/composer.json index fdaa186..c13f742 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "require-dev": { "statamic/cms": "^6.0", "mockery/mockery": "^1.4.4", - "orchestra/testbench": "^8.28 || ^9.6.1 || ^10.0", + "orchestra/testbench": "^9.6.1 || ^10.0", "phpunit/phpunit": "^10.5.35 || ^11.0" }, "autoload": { From cf71c71f575e043fbb857516e1637c4e6ca317ee Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 16:55:45 +0100 Subject: [PATCH 03/11] Global variables are always stored separately in v6 --- src/GlobalSetMigrator.php | 27 ++++----------------------- tests/MigrateGlobalSetTest.php | 30 +++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/GlobalSetMigrator.php b/src/GlobalSetMigrator.php index 32910cf..5045b06 100644 --- a/src/GlobalSetMigrator.php +++ b/src/GlobalSetMigrator.php @@ -57,15 +57,8 @@ protected function migrateGlobalSetSchema() ->forget('fieldset') ->forget('id'); - $meta = $this->set->only($metaKeys); - $data = $this->set->except($metaKeys); - - if ($this->isMultisite()) { - $this->set = $meta; - $this->localizedSets = $this->migrateLocalizedSets($data); - } else { - $this->set = $meta->put('data', $this->migrateSetData($data)); - } + $this->localizedSets = $this->migrateLocalizedSets($this->set->except($metaKeys)); + $this->set = $this->set->only($metaKeys); return $this; } @@ -125,24 +118,12 @@ protected function migrateSetData($data) * @return $this */ protected function saveMigratedSet() - { - if ($this->isMultisite()) { - $this->saveLocalizedSets(); - } - - return $this->saveMigratedYaml($this->set); - } - - /** - * Save migrated localized sets. - * - * @return $this - */ - protected function saveLocalizedSets() { collect($this->localizedSets)->each(function ($set, $locale) { $this->saveMigratedYaml($set, base_path("content/globals/{$locale}/{$this->handle}.yaml")); }); + + return $this->saveMigratedYaml($this->set); } /** diff --git a/tests/MigrateGlobalSetTest.php b/tests/MigrateGlobalSetTest.php index ab7dd4c..017c77f 100644 --- a/tests/MigrateGlobalSetTest.php +++ b/tests/MigrateGlobalSetTest.php @@ -25,6 +25,11 @@ protected function newPath() return Path::tidy(base_path('content/globals/main.yaml')); } + protected function variablesPath() + { + return Path::tidy(base_path('content/globals/default/main.yaml')); + } + protected function blueprintPath() { return Path::tidy(resource_path('blueprints/globals/main.yaml')); @@ -33,23 +38,26 @@ protected function blueprintPath() private function migrateGlobalSet($globalSet) { $this->assertFileNotExists($this->newPath()); + $this->assertFileNotExists($this->variablesPath()); $this->files->put($this->originalPath(), YAML::dump($globalSet)); $this->artisan('statamic:migrate:global-set', ['handle' => 'main']); $this->assertFileExists($this->newPath()); + $this->assertFileExists($this->variablesPath()); - return YAML::parse($this->files->get($this->newPath())); + return [YAML::parse($this->files->get($this->newPath())), YAML::parse($this->files->get($this->variablesPath()))]; } /** @test */ public function it_can_migrate_a_global_set() { $this->assertFileNotExists($this->newPath()); + $this->assertFileNotExists($this->variablesPath()); $this->assertFileNotExists($this->blueprintPath()); - $set = $this->migrateGlobalSet([ + [$set, $variables] = $this->migrateGlobalSet([ 'id' => '547c5873-ce9a-4b92-b6b8-a9c785f92fb4', 'title' => 'Global', 'fieldset' => 'globals', @@ -60,14 +68,16 @@ public function it_can_migrate_a_global_set() $this->assertEquals($set, [ 'title' => 'Global', - 'data' => [ - 'site_title' => 'Frederick\'s Swap Shop', - 'author' => 'Frederick Schwap', - 'fav_colour' => 'red', - ], + ]); + + $this->assertEquals($variables, [ + 'site_title' => 'Frederick\'s Swap Shop', + 'author' => 'Frederick Schwap', + 'fav_colour' => 'red', ]); $this->assertFileExists($this->newPath()); + $this->assertFileExists($this->variablesPath()); $this->assertFileExists($this->blueprintPath()); } @@ -76,7 +86,7 @@ public function it_can_implicitly_migrate_globals_blueprint() { $this->assertFileNotExists($this->blueprintPath()); - $set = $this->migrateGlobalSet([ + $this->migrateGlobalSet([ 'id' => '547c5873-ce9a-4b92-b6b8-a9c785f92fb4', 'title' => 'Global', 'site_title' => 'Frederick\'s Swap Shop', @@ -93,15 +103,17 @@ public function it_migrates_without_fieldset_when_one_does_not_exist() $this->files->delete(base_path('site/settings/fieldsets/globals.yaml')); $this->assertFileNotExists($this->newPath()); + $this->assertFileNotExists($this->variablesPath()); $this->assertFileNotExists($this->blueprintPath()); - $set = $this->migrateGlobalSet([ + $this->migrateGlobalSet([ 'title' => 'Global', 'site_title' => 'Frederick\'s Swap Shop', 'author' => 'Frederick Schwap', ]); $this->assertFileExists($this->newPath()); + $this->assertFileExists($this->variablesPath()); $this->assertFileNotExists($this->blueprintPath()); } } From 3ff807d99bb7c0a5a887292c4b998d5fa8c9648e Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 17:04:17 +0100 Subject: [PATCH 04/11] `date_format` config option has been removed. Related: https://github.com/statamic/cms/pull/11566 --- src/SettingsMigrator.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/SettingsMigrator.php b/src/SettingsMigrator.php index 73d20f1..5a90ee2 100644 --- a/src/SettingsMigrator.php +++ b/src/SettingsMigrator.php @@ -67,7 +67,6 @@ protected function migrateCp() Configurator::file($configFile = 'statamic/cp.php') ->set('start_page', $this->migrateStartPage($cp['start_page'] ?? false)) - ->set('date_format', $cp['date_format'] ?? false) ->merge('widgets', $cp['widgets'] ?? []) ->set('pagination_size', $cp['pagination_size'] ?? false) ->ifNoChanges($this->throwNoChangesException($configFile)); From c17a18b2443f4e7428e16e83d7d6a8117e9ab137 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 17:04:33 +0100 Subject: [PATCH 05/11] set minimum-stability --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c13f742..7e1094d 100644 --- a/composer.json +++ b/composer.json @@ -41,6 +41,6 @@ "pixelfear/composer-dist-plugin": true } }, - "minimum-stability": "stable", + "minimum-stability": "dev", "prefer-stable": true } From 8c04f244348a3c78245e168edc550f85dd9d1234 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 17:13:44 +0100 Subject: [PATCH 06/11] Remove `date_format` assertion --- tests/MigrateSettingsTest.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/MigrateSettingsTest.php b/tests/MigrateSettingsTest.php index bfcc62e..2881f5c 100644 --- a/tests/MigrateSettingsTest.php +++ b/tests/MigrateSettingsTest.php @@ -83,11 +83,6 @@ public function it_migrates_cp_settings() EOT ); - $this->assertConfigFileContains('cp.php', <<<'EOT' - 'date_format' => 'Y-m-d', -EOT - ); - $this->assertConfigFileContains('cp.php', <<<'EOT' 'widgets' => [ 'getting_started', From 68a8cd57f231c97990187d54edcf5750b8ea6451 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 17:14:20 +0100 Subject: [PATCH 07/11] The `cp.php` config now imports a class. Update `assertConfigFileContains` --- tests/MigrateSettingsTest.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/MigrateSettingsTest.php b/tests/MigrateSettingsTest.php index 2881f5c..66e7317 100644 --- a/tests/MigrateSettingsTest.php +++ b/tests/MigrateSettingsTest.php @@ -276,11 +276,21 @@ protected function assertConfigFileContains($file, $content) { $config = config_path("statamic/{$file}"); - $beginning = <<<'EOT' + if ($file === 'cp.php') { + $beginning = <<<'EOT' + Date: Tue, 7 Oct 2025 17:36:54 +0100 Subject: [PATCH 08/11] Migrate dates to UTC --- src/ContentMigrator.php | 17 +++++++ tests/ContentMigratorTest.php | 90 +++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/src/ContentMigrator.php b/src/ContentMigrator.php index 2c75ed5..f79d6f9 100644 --- a/src/ContentMigrator.php +++ b/src/ContentMigrator.php @@ -2,7 +2,9 @@ namespace Statamic\Migrator; +use Carbon\Carbon; use Illuminate\Filesystem\Filesystem; +use Statamic\Fieldtypes\Date; use Statamic\Migrator\Exceptions\EmptyValueException; use Statamic\Support\Arr; use Statamic\Support\Str; @@ -207,6 +209,21 @@ protected function migrateField($handle, $value, $config = null) return $value; } + protected function migrateDateField($handle, $value, $config) + { + if (($originalTimezone = $this->getSetting('system.timezone', 'UTC')) !== 'UTC') { + $value = Carbon::parse($value, $originalTimezone) + ->utc() + ->format($config['format'] ?? Date::DEFAULT_DATETIME_FORMAT); + + if (is_numeric($value)) { + $value = (int) $value; + } + } + + return $value; + } + /** * Migrate assets field. * diff --git a/tests/ContentMigratorTest.php b/tests/ContentMigratorTest.php index 147dc55..808c85f 100644 --- a/tests/ContentMigratorTest.php +++ b/tests/ContentMigratorTest.php @@ -120,6 +120,96 @@ public function it_can_migrate_assets_fields_without_container_url() $this->assertEquals($expected, $content); } + /** @test */ + public function it_can_migrate_date_field() + { + $content = $this + ->setFields([ + 'date_field' => [ + 'type' => 'date', + ], + 'date_field_with_format' => [ + 'type' => 'date', + 'format' => 'Y/m/d', + ], + 'date_field_with_time' => [ + 'type' => 'date', + 'allow_time' => true, + ], + 'date_field_with_time_and_format' => [ + 'type' => 'date', + 'allow_time' => true, + 'format' => 'Y/m/d H:i', + ], + ]) + ->migrateContent([ + 'date_field' => '2018-01-01', + 'date_field_with_format' => '2018/01/01', + 'date_field_with_time' => '2018-01-01 12:00', + 'date_field_with_time_and_format' => '2018/01/01 12:00', + ]); + + $expected = [ + 'date_field' => '2018-01-01', + 'date_field_with_format' => '2018/01/01', + 'date_field_with_time' => '2018-01-01 12:00', + 'date_field_with_time_and_format' => '2018/01/01 12:00', + 'blueprint' => 'speaker' + ]; + + $this->assertEquals($expected, $content); + + $this->files->delete($this->sitePath('settings/system.yaml')); + } + + /** @test */ + public function it_can_migrate_date_field_when_previous_timezone_was_not_utc() + { + // -5-hour offset + $this->files->put($this->sitePath('settings/system.yaml'), <<setFields([ + 'date_field' => [ + 'type' => 'date', + ], + 'date_field_with_format' => [ + 'type' => 'date', + 'format' => 'Y/m/d', + ], + 'date_field_with_time' => [ + 'type' => 'date', + 'allow_time' => true, + ], + 'date_field_with_time_and_format' => [ + 'type' => 'date', + 'allow_time' => true, + 'format' => 'Y/m/d H:i', + ], + ]) + ->migrateContent([ + 'date_field' => '2018-01-01', + 'date_field_with_format' => '2018/01/01', + 'date_field_with_time' => '2018-01-01 12:00', + 'date_field_with_time_and_format' => '2018/01/01 12:00', + ]); + + $expected = [ + 'date_field' => '2018-01-01 05:00', + 'date_field_with_format' => '2018/01/01', + 'date_field_with_time' => '2018-01-01 17:00', + 'date_field_with_time_and_format' => '2018/01/01 17:00', + 'blueprint' => 'speaker' + ]; + + $this->assertEquals($expected, $content); + + $this->files->delete($this->sitePath('settings/system.yaml')); + } + /** @test */ public function it_can_migrate_term_fields() { From d2fc9396071e8060d25b9b34ade87611f2fe75fc Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 17:39:46 +0100 Subject: [PATCH 09/11] Migrate `display_timezone` setting Dates will be migrated into UTC, the app's timezone will remain as UTC, but the display_timezone will be set to the v2 app's timezone. --- src/SettingsMigrator.php | 1 + tests/MigrateSettingsTest.php | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/SettingsMigrator.php b/src/SettingsMigrator.php index 5a90ee2..1a790ce 100644 --- a/src/SettingsMigrator.php +++ b/src/SettingsMigrator.php @@ -124,6 +124,7 @@ protected function migrateSystem() Configurator::file($configFile = 'statamic/system.php') ->set('multisite', count($sites) > 1) + ->set('display_timezone', $system['timezone'] ?? null) ->ifNoChanges($this->throwNoChangesException($configFile)); return $this; diff --git a/tests/MigrateSettingsTest.php b/tests/MigrateSettingsTest.php index 66e7317..c91937c 100644 --- a/tests/MigrateSettingsTest.php +++ b/tests/MigrateSettingsTest.php @@ -155,6 +155,11 @@ public function it_migrates_routes() /** @test */ public function it_migrates_system_settings() { + $this->files->put($this->sitePath('settings/system.yaml'), <<<'EOT' +timezone: 'America/New_York' +EOT + ); + $this->artisan('statamic:migrate:settings', ['handle' => 'system']); $this->assertSameWithNormalizedLineEndings(File::get(resource_path('sites.yaml')), @@ -166,6 +171,11 @@ public function it_migrates_system_settings() $this->assertConfigFileContains('system.php', <<<'EOT' 'multisite' => false, +EOT + ); + + $this->assertConfigFileContains('system.php', <<<'EOT' + 'display_timezone' => 'America/New_York', EOT ); } From c43824647e3092f23fea1e9cbe92a96caf1f3e58 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 17:43:39 +0100 Subject: [PATCH 10/11] require alpha 2 as a minimum otherwise, our cp.php assertion in MigrateSettingsTest will fail. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7e1094d..1637282 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "mustangostang/spyc": "dev-master#dfd9aadc1f5224065d55b42b712c7e99a50a3f4d" }, "require-dev": { - "statamic/cms": "^6.0", + "statamic/cms": "^v6.0.0-alpha.2", "mockery/mockery": "^1.4.4", "orchestra/testbench": "^9.6.1 || ^10.0", "phpunit/phpunit": "^10.5.35 || ^11.0" From 4dfba04a743b5f8e6ef40aa77ade72a6f941f18d Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 7 Oct 2025 17:58:05 +0100 Subject: [PATCH 11/11] try alpha 3. should fix the composer install issues. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1637282..9966425 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "mustangostang/spyc": "dev-master#dfd9aadc1f5224065d55b42b712c7e99a50a3f4d" }, "require-dev": { - "statamic/cms": "^v6.0.0-alpha.2", + "statamic/cms": "^v6.0.0-alpha.3", "mockery/mockery": "^1.4.4", "orchestra/testbench": "^9.6.1 || ^10.0", "phpunit/phpunit": "^10.5.35 || ^11.0"