Skip to main content
Table of contents

 

Continuing on from the Drupal 9 to Drupal 10 upgrade outline written earlier... Upgrading Drupal 9 to Drupal 10.  However, the point of difference is this article works through the actual action steps of the upgrade.

Planning and testing is critical for the Drupal to version 10 process to work successfully.  As some of the environments that I work in are deployed via CD/CI pipelines.

 

Resource settings

Type Version
Drush 11.6.0
Drupal 9.5.10

 

1. Download and enable the Upgrade Status

Run the following command to install the Upgrade Status module:

composer require 'drupal/upgrade_status:^4.0'

In the admin > extend area, enable the Upgrade Status module.  Or if you want to work in Terminal then run the command

drush en upgrade_status

 

Upgrade Status error

Whilst attempting to add Upgrade Status you experience the following error

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - drupal/upgrade_status[4.0.0, ..., 4.1.0] require nikic/php-parser ^4.0.0 -> found nikic/php-parser[v4.0.0, ..., v4.19.1] but the package is fixed to v5.0.2 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
    - Root composer.json requires drupal/upgrade_status ^4.0 -> satisfiable by drupal/upgrade_status[4.0.0, 4.1.0].

Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.

As outlined on Upgrade Status module page (https://www.drupal.org/project/upgrade_status), one option is to remove Drush and work through a re-install process.  However, rather than follow this process, I opted to run the following

composer require 'drupal/upgrade_status:^4.0' --with-all-dependencies

This action resulted in a downgrade of the offending package.  Plus installing 

Downgrading nikic/php-parser (v5.0.2 => v4.19.1)

 

2. Review the current status of the Drupal environment

As the Upgrade Status module has been enabled, you can review the status of your environment.  Go to 

{path}/admin/reports/upgrade-status

Current overview

Gather data Fix incompatibilities Relax

Drupal core and hosting environment 

Requirement Status
Drupal core should be at least 9.4.x Version 9.5.10.
PHP version should be at least 8.1.0. Before updating to PHP 8, use $ composer why-not php 8.1 to check if any projects need updating for compatibility. Also check custom projects manually. Version 8.1.18
Database JSON support required Supported.
Invalid permissions will trigger runtime exceptions in Drupal 10. Permissions should be defined in a permissions.yml file or a permission callback.
None found.
Deprecated or obsolete core extensions installed. These will be removed in the next major version. Color (read more), HAL (read more), RDF (read more), Stable (read more)

Modules to be removed as they're not installed

Module Status Version
Google Authenticator login (ga_login) uninstalled 8.x-1.0-alpha7
Search API Synonym (search_api_synonym) uninstalled N/A
Webform (webform) uninstalled 6.1.5

Modules to be updated

Module Status Version
Menu Breadcrumb (menu_breadcrumb) installed 8.x-1.16
Title length (title_length) installed 8.x-1.2

 

3. Remove uninstalled modules

As noted above, the following modules need to be removed:

  • Google Authenticator login (ga_login)
  • Media Delete media (media_delete_all)
  • Search API Synonym (search_api_synonym)
  • Webform (webform)

If you have installed these using the correct method - that is using composer, then using the module alias references you can use Terminal.  Run the composer remove command line

composer remove drupal/ga_login
composer remove drupal/media_delete_all
composer remove drupal/search_api_synonym
composer remove drupal/webform

 

4. Update flagged modules & themes

Modules that require upgrading

Update the contrib modules that were identified through the upgrade status module noted above:

  • Menu Breadcrumb (menu_breadcrumb)
  • Title length (title_length)

The following Drush commands were run to install the listed module

composer require 'drupal/menu_breadcrumb:^2.0@alpha' 'drupal/title_length:^2.0@RC'

Modules that need to be reviewed

By reviewed these modules either needed to be removed or upgraded.  

  • Color - will be removed as not required
  • HAL
  • RDF

Each of the above modules have been removed from core... and are available as contrib projects.

Module Version URL Drupal version
Color 1.0.3 https://www.drupal.org/project/color ^9.4
HAL 2.0 https://www.drupal.org/project/hal ^10
RDF ^2.1 https://www.drupal.org/project/rdf ^9.4 || ^10.0

In this instance, only HAL will be kept and therefore upgraded.  But only when Drupal has been upgraded to 10 in this environment.

composer require 'drupal/hal:^2.0'

Command line uninstalling the others

drush pm:uninstall color
drush pm:uninstall hal
drush pm:uninstall rdf

Module that need to collaborate with maintainers

The modules that fall under this category are a couple of custom modules and one contrib module.

Module Version URL Drupal version
Content import 8.x-9.3 https://www.drupal.org/project/contentimport ^8.7.7 || ^9

Note on the page is this project is not covered by the security advisory policy.  Given it's status and the requirement for it on the site is not required.  It will be removed.

drush pm:uninstall contentimport

The Content Import module also needed to be removed

composer remove drupal/contentimport

 

Themes that need to be removed

The following themes need to be removed as they have been deprecated

  • Bartik
  • Stable

However, Stable in Drupal 10 is Stable9.  Note, if any of your themes are dependant on either of these, you will to act on this first before you can uninstall the theme.

drush theme:uninstall bartik
drush theme:uninstall stable

 

5. Review the server environment

As completed earlier, cross check that the changes have removed the 'Deprecated or obsolete core extensions installed. These will be removed in the next major version' notice.

Requirement Status
Drupal core should be at least 9.4.x Version 9.5.10.
PHP version should be at least 8.1.0. Before updating to PHP 8, use $ composer why-not php 8.1 to check if any projects need updating for compatibility. Also check custom projects manually. Version 8.1.18
Database JSON support required Supported.
Invalid permissions will trigger runtime exceptions in Drupal 10. Permissions should be defined in a permissions.yml file or a permission callback.
None found.
Deprecated or obsolete core extensions installed. These will be removed in the next major version. None installed.

Notes:

  • Compatible with next major Drupal core version: 73 projects
  • Environment checks passed

Having completed the preliminary work, the site is ready to be upgraded to Drupal 10.

 

6. Preparation of the server directories

Now that you have prepared your site, your first step in the upgrade process is to temporarily add write access to protected files and directories:

chmod -R 777 sites/default
chmod 666 sites/default/*settings.php
chmod 666 sites/default/*services.yml

 

7. Update the composer.json file

composer require 'drupal/core-recommended:^10' 'drupal/core-composer-scaffold:^10' 'drupal/core-project-message:^10' --update-with-dependencies --no-update

As core-dev exists

composer require 'drupal/core-dev:^10' --dev --update-with-dependencies --no-update

 

8. Drush version

Check the current version of Drush being used, as Drupal 10 is best to run off Drush 12

drush --version

Response

Drush Commandline Tool 11.6.0

Prepare the update to version by

composer require 'drush/drush:^12' --no-update

 

9. Time to perform the update

With the preparation completed, let's run the update

composer update

 

Errors post running composer update

Your requirements could not be resolved to an installable set of packages.
  Problem 1
    - zendframework/zend-feed[2.11.0, ..., 2.12.0] require php ^5.6 || ^7.0 -> your php version (8.1.18) does not satisfy that requirement.
    - laminas/laminas-feed[2.12.0, ..., 2.12.3] require php ^5.6 || ^7.0 -> your php version (8.1.18) does not satisfy that requirement.
    - symfony-cmf/routing[1.4.0, ..., 1.4.1] require php ^5.3.9|^7.0 -> your php version (8.1.18) does not satisfy that requirement.
    - drupal/core[8.9.11, ..., 8.9.20] require php ^7.0.8 -> your php version (8.1.18) does not satisfy that requirement.
    - drupal/core[9.0.10, ..., 9.0.14] require php ^7.3 -> your php version (8.1.18) does not satisfy that requirement.
    - laminas/laminas-feed[2.13.0, ..., 2.14.1] require php ^7.3 || ~8.0.0 -> your php version (8.1.18) does not satisfy that requirement.
    - Root composer.json requires drupal/contentimport ^9.3 -> satisfiable by drupal/contentimport[9.3.0].
    - drupal/core-recommended 10.0.2 requires drupal/core 10.0.2 -> satisfiable by drupal/core[10.0.2].
    - Conclusion: don't install drupal/core 10.0.2 (conflict analysis result)
    - drupal/core-recommended 10.0.3 requires drupal/core 10.0.3 -> satisfiable by drupal/core[10.0.3].
    - Conclusion: don't install drupal/core 10.0.3 (conflict analysis result)
    - drupal/core-recommended 10.0.4 requires drupal/core 10.0.4 -> satisfiable by drupal/core[10.0.4].
    - Conclusion: don't install drupal/core 10.0.4 (conflict analysis result)
    - drupal/core-recommended 10.0.5 requires drupal/core 10.0.5 -> satisfiable by drupal/core[10.0.5].
    - Conclusion: don't install drupal/core 10.0.5 (conflict analysis result)
    - drupal/core-recommended 10.0.6 requires drupal/core 10.0.6 -> satisfiable by drupal/core[10.0.6].
    - Conclusion: don't install drupal/core 10.0.6 (conflict analysis result)
    - drupal/core-recommended 10.0.7 requires drupal/core 10.0.7 -> satisfiable by drupal/core[10.0.7].
    - Conclusion: don't install drupal/core 10.0.7 (conflict analysis result)
    - drupal/core-recommended 10.0.8 requires drupal/core 10.0.8 -> satisfiable by drupal/core[10.0.8].
    - Conclusion: don't install drupal/core 10.0.8 (conflict analysis result)
    - drupal/core-recommended 10.0.9 requires drupal/core 10.0.9 -> satisfiable by drupal/core[10.0.9].
    - Conclusion: don't install drupal/core 10.0.9 (conflict analysis result)
    - drupal/core-recommended 10.0.10 requires drupal/core 10.0.10 -> satisfiable by drupal/core[10.0.10].
    - Conclusion: don't install drupal/core 10.0.10 (conflict analysis result)
    - drupal/core-recommended 10.1.0 requires drupal/core 10.1.0 -> satisfiable by drupal/core[10.1.0].
    - Conclusion: don't install drupal/core 10.1.0 (conflict analysis result)
    - drupal/core-recommended 10.1.1 requires drupal/core 10.1.1 -> satisfiable by drupal/core[10.1.1].
    - Conclusion: don't install drupal/core 10.1.1 (conflict analysis result)
    - drupal/core-recommended 10.1.2 requires drupal/core 10.1.2 -> satisfiable by drupal/core[10.1.2].
    - Conclusion: don't install drupal/core 10.1.2 (conflict analysis result)
    - drupal/core-recommended 10.1.3 requires drupal/core 10.1.3 -> satisfiable by drupal/core[10.1.3].
    - Conclusion: don't install drupal/core 10.1.3 (conflict analysis result)
    - drupal/core[8.4.0, ..., 8.9.10] require symfony-cmf/routing ^1.4 -> satisfiable by symfony-cmf/routing[1.4.0, 1.4.1].
    - drupal/core[8.2.0, ..., 8.3.9] require symfony-cmf/routing ~1.4 -> satisfiable by symfony-cmf/routing[1.4.0, 1.4.1].
    - drupal/core[8.8.0, ..., 8.8.12] require zendframework/zend-feed ^2.12 -> satisfiable by laminas/laminas-feed[2.12.0, ..., 2.19.0], zendframework/zend-feed[2.12.0].
    - drupal/core-recommended 10.0.1 requires drupal/core 10.0.1 -> satisfiable by drupal/core[10.0.1].
    - Conclusion: don't install drupal/core 10.0.1 (conflict analysis result)
    - drupal/contentimport 9.3.0 requires drupal/core ^8 || ^9 -> satisfiable by drupal/core[8.0.0, ..., 8.9.20, 9.0.0, ..., 9.5.10].
    - You can only install one version of a package, so only one of these can be installed: drupal/core[8.0.0, ..., 8.9.20, 9.0.0, ..., 9.5.10, 10.0.0, ..., 10.1.3].
    - You can only install one version of a package, so only one of these can be installed: drupal/core[8.1.2, ..., 8.9.20, 9.0.0, ..., 9.5.10, 10.0.0, ..., 10.1.3].
    - You can only install one version of a package, so only one of these can be installed: drupal/core[8.1.8, ..., 8.9.20, 9.0.0, ..., 9.5.10, 10.0.0, ..., 10.1.3].
    - You can only install one version of a package, so only one of these can be installed: drupal/core[8.7.0, ..., 8.9.20, 9.0.0, ..., 9.5.10, 10.0.0, ..., 10.1.3].
    - drupal/core-recommended 10.0.0 requires drupal/core 10.0.0 -> satisfiable by drupal/core[10.0.0].
    - Root composer.json requires drupal/core-recommended ^10 -> satisfiable by drupal/core-recommended[10.0.0, ..., 10.1.3].
Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.

Solution to the above error

Whilst this didn't occur for this upgrade, an issue like this was based on not having removed a contrib module that couldn't be migrated to Drupal 10.  Make sure you have worked through all of the modules being flagged as an issue via the upgrade status.

Whilst adding this, I also added the following new modules:

Module Version URL
Gin 8.x-3.0-rc6 https://www.drupal.org/project/gin

To install the Gin theme use

composer require drupal/gin_toolbar:^1.0@rc drupal/gin:^3.0@rc

 

Back on track.

 

10. Update the database

Perform this action by running

drush updatedb

Response

 ------------------ ------------------------------- --------------- -------------------------------------------------------------------------------
  Module             Update ID                       Type            Description
 ------------------ ------------------------------- --------------- -------------------------------------------------------------------------------
  system             10100                           hook_update_n   10100 - Remove the year 2038 date limitation.
  block_content      10100                           hook_update_n   10100 - Update entity definition to handle revision routes.
  block_content      10200                           hook_update_n   10200 - Remove the unique values constraint from block content info fields.
  comment            10100                           hook_update_n   10100 - Remove the year 2038 date limitation.
  dblog              10100                           hook_update_n   10100 - Remove the year 2038 date limitation.
  dblog              10101                           hook_update_n   10101 - Converts the 'wid' of the 'watchdog' table to a big integer.
  history            10100                           hook_update_n   10100 - Remove the year 2038 date limitation.
  migrate            10100                           hook_update_n   10100 - Remove the year 2038 date limitation.
  redirect           8109                            hook_update_n   8109 - Enable reset button for Redirect View.
  title_length       8002                            hook_update_n   8002 - Activate node_title_length submodule for active projects.
  tracker            10100                           hook_update_n   10100 - Remove the year 2038 date limitation.
  user               10000                           hook_update_n   10000 - Remove non-existent permissions created by migrations.
  block_content      block_library_view_permission   post-update     Update block_content 'block library' view permission.
  block_content      move_custom_block_library       post-update     Moves the custom block library to Content.
  block_content      sort_permissions                post-update     Update permissions for users with "administer blocks" permission.
  editor             image_lazy_load                 post-update     Enable filter_image_lazy_load if editor_file_reference is enabled.
  file               add_permissions_to_roles        post-update     Grant all non-anonymous roles the 'delete own files' permission.
  media              oembed_loading_attribute        post-update     Add the oEmbed loading attribute setting to field formatter instances.
  responsive_image   image_loading_attribute         post-update     Add the image loading settings to responsive image field formatter instances.
  responsive_image   order_multiplier_numerically    post-update     Re-order mappings by breakpoint ID and descending numeric multiplier order.
  system             enable_password_compatibility   post-update     Enable the password compatibility module.
  system             linkset_settings                post-update     Add new menu linkset endpoint setting.
  system             timestamp_formatter             post-update     Update timestamp formatter settings for entity view displays.
  text               allowed_formats                 post-update     Add allowed_formats setting to existing text fields.
  views              boolean_custom_titles           post-update     Update Views config schema to make boolean custom titles translatable.
  views              fix_revision_id_part            post-update     Fix '-revision_id' replacement token syntax.
  views              oembed_eager_load               post-update     Add eager load option to all oembed type field configurations.
  views              responsive_image_lazy_load      post-update     Add lazy load options to all responsive image type field configurations.
  views              timestamp_formatter             post-update     Update timestamp formatter settings for views.
 ------------------ ------------------------------- --------------- -------------------------------------------------------------------------------

 

Reset the permissions that were altered earlier

If completed, then change the permissions that were altered at the start

chmod -R 755 sites/default
chmod 644 sites/default/*settings.php
chmod 644 sites/default/*services.yml

 

Review the Status Report

If you have any issues, read through the Errors / issues below.  You might be experiencing similar issues to us.

Otherwise, if all going well, then let's cross check status report (Admin > Reports > Status Report).

 

Status Report - Errors

The first batch of errors:

Area Notes or details
Search API The following search servers are not available: General, Related
Solr Server General Solr not reachable
Solr server General is not reachable.
Solr Server Related Solr not reachable
Solr server Related is not reachable.

The Solr errors I'll leave as there related to the server.

 

Status Report - warnings

Area Notes or details Solution
Color Field library: jQuery Simple Color

Missing

If you want to use the Simple Color widget, you must download the jQuery Simple Color library and copy it to /app/web/libraries/jquery-simple-color/

Download library from https://github.com/recurser/jquery-simple-color
Color Field library: Spectrum

Missing

If you want to use the Spectrum widget, you must download the Spectrum Library and copy it to /app/web/libraries/spectrum/

Download the library from https://github.com/bgrins/spectrum
Configuration files

Protection disabled

  • The file sites/default/settings.php is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.
  • The file sites/default/settings.local.php is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.
  • The file sites/default/services.yml is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.

The permissions noted above are correct.

If you see this error, then check if hardening has been activated.  If so, you'll need to add to your settings.php file the following line

$settings['skip_permissions_hardening'] = FALSE;
Deprecated modules enabled Deprecated modules found: Activity Tracker. Uninstall listed modules
Media It is potentially insecure to display oEmbed content in a frame that is served from the same domain as your main Drupal site, as this may allow execution of third-party code. You can specify a different domain for serving oEmbed content here.  
SameSite cookie attribute

Not set

This attribute should be explicitly set to Lax, Strict or None. If set to None then the request must be made via HTTPS. See PHP documentation

 
Transaction isolation level

REPEATABLE-READ

The recommended level for Drupal is "READ COMMITTED". See the setting MySQL transaction isolation level page for more information.

 

 

 

Errors / issues

Theme issue

When working on updating a custom theme from Drupal 9 to Drupal 10, the theme used Stable as a base theme.  However, this needs to be changed to Stable9 to work in Drupal 10.  However, I came across a curious issue I had the following error.

Drupal\Core\Theme\MissingThemeDependencyException: Base theme stable9 has not been installed. in Drupal\Core\Theme\ThemeInitialization->getActiveThemeByName() (line 122 of core/lib/Drupal/Core/Theme/ThemeInitialization.php)

To resolve this error, I manually installed Stable9 using Drush

drush theme:enable stable9

Response

[success] Successfully installed theme: stable9

 

Related articles