This section provides information for developers who wish to help collaborate and improve Drupal WxT.
This is the multi-page printable view of this section. Click here to print.
Development
- 1: Architecture
- 2: Composer
- 3: Configuration Management
- 4: Performance
- 4.1: PostgreSQL
- 4.2: Redis
- 4.3: Varnish
- 5: Release Process
- 6: Theming
- 7: Versioning
1 - Architecture
The goal of Drupal WxT since the 4.1.x line is to make the installation profile very minimal by default but providing additional extensions that can be enabled as desired.
What WxT offers is some light enhancements to Drupal Core, mainly around security and performance, and integration with the Web Experience Toolkit. By default, the distribution offers minimal functionality to allow full customizations by users. However a great deal of optional extensions are available that can provide additional functionality generally beneficial to Government departments.
Note: In the future we are looking into providing a list of community modules that are build to work with the distribution but are “out of tree”.
All of the optional modules are located in the wxt_ext
folder named after WxT Extend and can be enabled during the initial site installation by passing the following flag via the drush cli:
wxt_extension_configure_form.select_all='TRUE'
Note: If you wish to only install the minimum set of dependencies please remove the
wxt_extension_configure_form.select_all='TRUE'
flag in its entirety.
In order to provide a list of the optional enabled extensions during the installation that can be checked, all that any module has to do is provide a modulename.wxt_extension.yml
file in their root and they will be picked as installable during the profile install and also respond to the additional drush flag discussed above.
For more information on some of the history leading to this design:
2 - Composer
We highly recommend using our Composer Project Template to build and maintain your WxT derived project’s codebase.
Getting Started
The following command is all you need to get started:
composer create-project drupalwxt/wxt-project:5.1.1 <site-name>
Note: For development you may also specify a branch using
drupalwxt/wxt-project:5.1.x-dev
.
You can see a working example of a fully generated Composer Project Template over at:
Where the following is the command that was used for the initial generation:
composer create-project drupalwxt/wxt-project:5.1.1 site-wxt
Note: Remember to keep the
composer.json
andcomposer.lock
files that exist abovedocroot
in source control as they are controlling your dependencies.
Maintenance
List of common commands are as follows:
Task | Composer |
---|---|
Installing a contrib project (latest version) | composer require drupal/PROJECT |
Installing a contrib project (specific version) | composer require drupal/PROJECT:1.0.0-beta5 |
Updating all projects including Drupal Core | composer update |
Updating a single contrib project | composer update drupal/PROJECT_NAME |
Updating Drupal Core | composer update drupal/core |
Note: Composer is a dependency manager and helps us keep track of what code and at what version our application relies on so that it always get installed the right way on every copy of that application.
Specifying a version
A specific version can be specified from the cli:
composer require drupal/<modulename>:<version>
However please note if you specify a branch, such as 1.x you must add -dev
to the end of the version:
composer require drupal/token:1.x-dev
Source Control
Taking a look at the .gitignore
file, you will discover that certain directories, including all those directories containing contributed projects, are excluded from source control which is by design.
Note: Unlike Drush in a Composer derived project you should never commit your install dependencies to source control.
Composer will create composer.lock
file, which is a list of dependencies that were installed, and in which versions.
Note: In general you should always commit your
composer.lock
file to source control so that others via a quickcomposer install
can have everything installed along with the correct versions specified in thecomposer.lock
file.
How to update Drupal Core?
Please don’t add drupal/core
to your project’s composer.json since WxT manages Drupal Core for you along with the series of patches on top of it.
For example:
drupalwxt/wxt:~5.2.0
will require Drupal Core 10.2.xdrupalwxt/wxt:~5.1.0
will require Drupal Core 10.1.x
When you need to update Drupal Core as an example from 10.1.x to 10.2.x, all you would do is change your requirement for drupalwxt/wxt
in your composer.json
file:
composer require --no-update drupalwxt/wxt:~5.2.0
composer update
Compatibility table
WxT version | Drupal Core version | Drush version | PHP version |
---|---|---|---|
5.2.x | 10.2.x | >=12.4 | 8.2 |
5.1.x | 10.1.x | >=12.1 | 8.1 |
3 - Configuration Management
Drupal WxT thanks to the work done by the Acquia Team is able to use advanced configuration management strategies.
At the moment this remains an opt-in process and you will have to add the
following modules to your composer.json
before you add the code snippet
below to your settings.php
file.
Once enabled all default configuration will be stored in /sites/default/files/config/default/
and then depending on your environment additionally configuration splits can
be leveraged depending on your SDLC
.
/**
* Configuration Split for Configuration Management
*
* WxT is following the best practices given by Acquia for configuration
* management. The "default" configuration directory should be shared between
* all multi-sites, and each multisite will override this selectively using
* configuration splits.
*
* To disable this functionality simply set the following parameters:
* $wxt_override_config_dirs = FALSE;
* $settings['config_sync_directory'] = $dir . "/config/$site_dir";
*
* See https://github.com/acquia/blt/blob/12.x/settings/config.settings.php
* for more information.
*/
use Drupal\wxt\Robo\Common\EnvironmentDetector;
if (!isset($wxt_override_config_dirs)) {
$wxt_override_config_dirs = TRUE;
}
if ($wxt_override_config_dirs) {
$config_directories['sync'] = $repo_root . "/var/www/html/sites/default/files/config/default";
$settings['config_sync_directory'] = $repo_root . "/var/www/html/sites/default/files/config/default";
}
$split_filename_prefix = 'config_split.config_split';
if (isset($config_directories['sync'])) {
$split_filepath_prefix = $config_directories['sync'] . '/' . $split_filename_prefix;
}
else {
$split_filepath_prefix = $settings['config_sync_directory'] . '/' . $split_filename_prefix;
}
/**
* Set environment splits.
*/
$split_envs = [
'local',
'dev',
'test',
'qa',
'prod',
'ci',
];
foreach ($split_envs as $split_env) {
$config["$split_filename_prefix.$split_env"]['status'] = FALSE;
}
if (!isset($split)) {
$split = 'none';
if (EnvironmentDetector::isLocalEnv()) {
$split = 'local';
}
if (EnvironmentDetector::isCiEnv()) {
$split = 'ci';
}
if (EnvironmentDetector::isDevEnv()) {
$split = 'dev';
}
elseif (EnvironmentDetector::isTestEnv()) {
$split = 'test';
}
elseif (EnvironmentDetector::isQaEnv()) {
$split = 'qa';
}
elseif (EnvironmentDetector::isProdEnv()) {
$split = 'prod';
}
}
if ($split != 'none') {
$config["$split_filename_prefix.$split"]['status'] = TRUE;
}
/**
* Set multisite split.
*/
// $config["$split_filename_prefix.SITENAME"]['status'] = TRUE;
4 - Performance
Below are some recommended settings that improve the performance of Drupal WxT sites.
4.1 - PostgreSQL
To properly configure PostgreSQL with Drupal you should ensure the following configuration is used.
Note: Some customizations might be necessary depending on your individual requirements.
postgresqlConfiguration:
listenAddresses: "'*'"
maxConnections: "200"
sharedBuffers: 512MB
workMem: 2048MB
effectiveCacheSize: 512MB
effectiveIoConcurrency: "100"
maintenanceWorkMem: 32MB
minWalSize: 512MB
maxWalSize: 512MB
walBuffers: 8048kB
byteaOutput: "'escape'"
hugePages: "off"
walLevel: "replica"
maxWalSenders: "0"
synchronousCommit: "on"
walKeepSegments: "130"
checkpointTimeout: "'15 min'"
checkpointCompletionTarget: "0.9"
walCompression: "on"
walWriterDelay: 200ms
walWriterFlushAfter: 1MB
bgwriterDelay: 200ms
bgwriterLruMaxpages: "100"
bgwriterLruMultiplier: "2.0"
bgwriterFlushAfter: "0"
maxWorkerProcesses: "8"
maxParallelWorkersPerGather: "4"
maxParallelWorkers: "4"
Note: The above is written in yaml syntax which will work for both Docker Compose and Kubernetes Helm Charts. For the
postgresql.conf
file itself without using these tools simply find the_
counterpart.
Queries leveraging ILIKE
There is a known PostgreSQL performance issue that exists in Drupal and is related to leveraging queries with ILIKE
.
This issue is particularly noticeable in relation to the path_alias table.
There are patches being worked on to handle this in Drupal core but a very quick fix can be implemented leveraging pg_trgm.
There is a great blog article listed below which goes over this issue in more detail.
The instructions are a bit outdated so the updated syntax to enter in psql is given below:
CREATE EXTENSION pg_trgm;
CREATE INDEX path_alias__alias_trgm_gist_idx ON path_alias USING gist (alias gist_trgm_ops);
CREATE INDEX path_alias__path_trgm_gist_idx ON path_alias USING gist (path gist_trgm_ops);
ANALYZE path_alias;
4.2 - Redis
To properly configure Redis with Drupal you should ensure the following configuration is added to your settings.php
file.
Note: Some customizations might be necessary depending on your individual requirements.
if (extension_loaded('redis')) {
// Set Redis as the default backend for any cache bin not otherwise specified.
$settings['cache']['default'] = 'cache.backend.redis';
$settings['redis.connection']['interface'] = 'PhpRedis';
$settings['redis.connection']['scheme'] = 'http';
$settings['redis.connection']['host'] = 'localhost';
$settings['redis.connection']['port'] = '6379';
$settings['redis.connection']['password'] = getenv('REDIS_PASSWORD') ?: '';
$settings['redis.connection']['persistent'] = FALSE;
// Allow the services to work before the Redis module itself is enabled.
$settings['container_yamls'][] = 'modules/contrib/redis/example.services.yml';
$settings['container_yamls'][] = 'modules/contrib/redis/redis.services.yml';
// Manually add the classloader path, this is required for the container cache bin definition below
// and allows to use it without the redis module being enabled.
$class_loader->addPsr4('Drupal\\redis\\', 'modules/contrib/redis/src');
$settings['bootstrap_container_definition'] = [
'parameters' => [],
'services' => [
'redis.factory' => [
'class' => 'Drupal\redis\ClientFactory',
],
'cache.backend.redis' => [
'class' => 'Drupal\redis\Cache\CacheBackendFactory',
'arguments' => ['@redis.factory', '@cache_tags_provider.container', '@serialization.phpserialize'],
],
'cache.container' => [
'class' => '\Drupal\redis\Cache\PhpRedis',
'factory' => ['@cache.backend.redis', 'get'],
'arguments' => ['container'],
],
'cache_tags_provider.container' => [
'class' => 'Drupal\redis\Cache\RedisCacheTagsChecksum',
'arguments' => ['@redis.factory'],
],
'serialization.phpserialize' => [
'class' => 'Drupal\Component\Serialization\PhpSerialize',
],
],
];
/** Optional prefix for cache entries */
$settings['cache_prefix'] = 'drupal_';
// Always set the fast backend for bootstrap, discover and config, otherwise
// this gets lost when redis is enabled.
$settings['cache']['bins']['bootstrap'] = 'cache.backend.chainedfast';
$settings['cache']['bins']['discovery'] = 'cache.backend.chainedfast';
$settings['cache']['bins']['config'] = 'cache.backend.chainedfast';
// Use for all bins otherwise specified.
$settings['cache']['default'] = 'cache.backend.redis';
// Use for all queues unless otherwise specified for a specific queue.
$settings['queue_default'] = 'queue.redis';
// Or if you want to use reliable queue implementation.
// $settings['queue_default'] = 'queue.redis_reliable';
// Use this to only use Redis for a specific queue.
// $settings['queue_service_aggregator_feeds'] = 'queue.redis';
// Use this to use reliable queue implementation.
// $settings['queue_service_aggregator_feeds'] = 'queue.redis_reliable';
}
4.3 - Varnish
To properly configure Varnish with Drupal you should ensure the following configuration is your default.vcl
file.
Note: Some customizations might be necessary depending on your individual requirements.
vcl 4.0;
import std;
import directors;
backend nginx {
.host = "hostname-nginx";
.host_header = "hostname-nginx";
.port = "80";
}
sub vcl_init {
new backends = directors.round_robin();
backends.add_backend(nginx);
}
sub vcl_recv {
set req.http.X-Forwarded-Host = req.http.Host;
if (!req.http.X-Forwarded-Proto) {
set req.http.X-Forwarded-Proto = "http";
}
# Answer healthcheck
if (req.url == "/_healthcheck" || req.url == "/healthcheck.txt") {
return (synth(700, "HEALTHCHECK"));
}
set req.backend_hint = backends.backend();
# Answer healthcheck
if (req.url == "/_healthcheck" || req.url == "/healthcheck.txt") {
return (synth(700, "HEALTHCHECK"));
}
set req.backend_hint = backends.backend();
# Always cache certain file types
# Remove cookies that Drupal doesn't care about
if (req.url ~ "(?i)\.(asc|dat|tgz|png|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") {
unset req.http.Cookie;
} else if (req.http.Cookie) {
set req.http.Cookie = ";" + req.http.Cookie;
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE)=", "; \1=");
set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
if (req.http.Cookie == "") {
unset req.http.Cookie;
} else {
return (pass);
}
}
# If POST, PUT or DELETE, then don't cache
if (req.method == "POST" || req.method == "PUT" || req.method == "DELETE") {
return (pass);
}
# Happens before we check if we have this in cache already.
#
# Typically you clean up the request here, removing cookies you don't need,
# rewriting the request, etc.
return (hash);
#return (pass);
}
sub vcl_backend_fetch {
# NEW
set bereq.http.Host = "hostname-nginx";
# Don't add 127.0.0.1 to X-Forwarded-For
set bereq.http.X-Forwarded-For = regsub(bereq.http.X-Forwarded-For, "(, )?127\.0\.0\.1$", "");
}
sub vcl_backend_response {
if (beresp.http.Location) {
set beresp.http.Location = regsub(
beresp.http.Location,
"^https?://[^/]+/",
bereq.http.X-Forwarded-Proto + "://" + bereq.http.X-Forwarded-Host + "/"
);
}
# Only cache select response codes
if (beresp.status == 200 || beresp.status == 203 || beresp.status == 204 || beresp.status == 206 || beresp.status == 300 || beresp.status == 301 || beresp.status == 404 || beresp.status == 405 || beresp.status == 410 || beresp.status == 414 || beresp.status == 501) {
# Cache for 5 minutes
set beresp.ttl = 5m;
set beresp.grace = 12h;
set beresp.keep = 24h;
} else {
set beresp.ttl = 0s;
}
}
sub vcl_deliver {
# Remove identifying information
unset resp.http.Server;
unset resp.http.X-Powered-By;
unset resp.http.X-Varnish;
unset resp.http.Via;
# Comment these for easier Drupal cache tag debugging in development.
unset resp.http.Cache-Tags;
unset resp.http.X-Drupal-Cache-Contexts;
# Add Content-Security-Policy
# set resp.http.Content-Security-Policy = "default-src 'self' *.example.ca *.example.ca; style-src 'self' 'unsafe-inline' *.example.ca https://fonts.googleapis.com; script-src 'self' 'unsafe-inline' 'unsafe-eval' *.example.ca *.adobedtm.com use.fontawesome.com blob:; connect-src 'self' *.example.ca *.omtrdc.net *.demdex.net *.everesttech.net; img-src 'self' *.example.ca *.omtrdc.net *.demdex.net *.everesttech.net data:; font-src 'self' *.example.ca https://fonts.gstatic.com";
# Add CORS Headers
# if (req.http.Origin ~ "(?i)\.example\.ca$") {
# if (req.url ~ "\.(ttd|woff|woff2)(\?.*)?$") {
# set resp.http.Access-Control-Allow-Origin = "*";
# set resp.http.Access-Control-Allow-Methods = "GET";
# }
# }
# Add X-Frame-Options
if (req.url ~ "^/livechat" || req.url ~ "^/(en/|fr/)?entity-browser/") {
set resp.http.X-Frame-Options = "SAMEORIGIN";
} else {
set resp.http.X-Frame-Options = "DENY";
}
set resp.http.X-Content-Type-Options = "nosniff";
set resp.http.X-XSS-Protection = "1; mode=block";
# Happens when we have all the pieces we need, and are about to send the
# response to the client.
#
# You can do accounting or modifying the final object here.
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
# Handle errors
if ( (resp.status >= 500 && resp.status <= 599)
|| resp.status == 400
|| resp.status == 401
|| resp.status == 403
|| resp.status == 404) {
return (synth(resp.status));
}
}
sub vcl_synth {
# Remove identifying information
unset resp.http.Server;
unset resp.http.X-Powered-By;
unset resp.http.X-Varnish;
unset resp.http.Via;
# Add Content-Security-Policy
# set resp.http.Content-Security-Policy = "default-src 'self' *.example.ca; style-src 'self' 'unsafe-inline' *.example.ca; script-src 'self' 'unsafe-inline' 'unsafe-eval' *.example.ca *.adobedtm.com use.fontawesome.com blob:; connect-src 'self' *.example.ca *.omtrdc.net *.demdex.net *.everesttech.net; img-src 'self' *.example.ca data:;";
# set resp.http.X-Content-Type-Options = "nosniff";
# set resp.http.X-Frame-Options = "DENY";
# set resp.http.X-XSS-Protection = "1; mode=block";
# if (resp.status >= 500 && resp.status <= 599) {
# set resp.http.Content-Type = "text/html; charset=utf-8";
# synthetic(std.fileread("/data/configuration/varnish/errors/503.html"));
# return (deliver);
# } elseif (resp.status == 400) { # 400 - Bad Request
# set resp.http.Content-Type = "text/html; charset=utf-8";
# synthetic(std.fileread("/data/configuration/varnish/errors/400.html"));
# return (deliver);
# } elseif (resp.status == 401) { # 401 - Unauthorized
# set resp.http.Content-Type = "text/html; charset=utf-8";
# synthetic(std.fileread("/data/configuration/varnish/errors/401.html"));
# return (deliver);
# } elseif (resp.status == 403) { # 403 - Forbidden
# set resp.http.Content-Type = "text/html; charset=utf-8";
# synthetic(std.fileread("/data/configuration/varnish/errors/403.html"));
# return (deliver);
# } elseif (resp.status == 404) { # 404 - Not Found
# set resp.http.Content-Type = "text/html; charset=utf-8";
# synthetic(std.fileread("/data/configuration/varnish/errors/404.html"));
# return (deliver);
# } else
if (resp.status == 700) { # Respond to healthcheck
set resp.status = 200;
set resp.http.Content-Type = "text/plain";
synthetic ( {"OK"} );
return (deliver);
}
}
##
# ERROR HANDLING
##
# sub vcl_backend_error {
# set beresp.http.Content-Type = "text/html; charset=utf-8";
# synthetic(std.fileread("/data/configuration/varnish/errors/503.html"));
# return (deliver);
# }
5 - Release Process
Select a version number
WxT releases are numbered using a form of semantic versioning. More information can be found in our Versioning page.
MAJOR.FEATURE.SPRINT
In general, when preparing a release: increment the FEATURE when Drupal Core has a major release (ie. 9.5.x to 10.0.x) otherwise simply increment the SPRINT number.
Create an issue on GitHub.com
Create an issue in the Drupal WxT project on GitHub for release tracking, title it Release x.x.x
(where x.x.x is the incremented version number).
This issue should contain the following checklist as well as any other related steps or information regarding preparing the release.
See the [full release documentation](https://drupalwxt.github.io/docs/development/release-process/) for more detail.
- [ ] All related projects (wxt_library and wxt_bootstrap) tagged and released on GitHub.com and Drupal.org
- [ ] Version number selected
- [ ] CHANGELOG.md updated
- [ ] composer.json updated
- [ ] Run version.sh for hook_updates and wxt contrib
- [ ] CI build passes
- [ ] Releases tagged and pushed to GitHub.com and Drupal.org
- [ ] WxT released on Drupal.org (https://drupalwxt.github.io/docs/development/release-process/#release)
- [ ] Add changelog information to published tag once CI is done
Update changelog
Ensure the changelog contains an entry for the release and is updated as issues and changes are resolved (in the next steps or when committing code / changes).
Review dependent wxt modules
- Review contrib modules in composer.json (or in a site install; extend->update) and update as necessary.
- If necessary, tag wxt_library and update wxt’s
composer.json
file - If necessary, tag wxt_bootstrap and update wxt’s
composer.json
file
All projects must be released on drupal.org (and github).
Note: Changes to
composer.json
file (specifically dev dependencies and repositories) should be mentioned in the CHANGELOG.
Drupal.org version
Drupal.org does not currently support semantic versioning. Instead, the version number on drupal.org is 10.x-X.YZZ
, where:
X = MAJOR
Y = FEATURE
ZZ = SPRINT
(two digits - add leading zero for < 10)
Check composer.json
If the wxt dependent modules are updated, we need to reflect this in wxt composer.json
and the CHANGELOG.
git clone https://github.com/drupalwxt/wxt.git
- Confirm or update that it’s using appropriate tags of wxt_library + wxt_bootstrap (
composer.json
) - Push to github.com and drupal.org repositories any changes
- Ensure GitHub Actions build passes
Tag WxT
GitHub.com and Drupal.org
git tag MAJOR.FEATURE.SPRINT
git push $GITHUB_REMOTE MAJOR.FEATURE.SPRINT
Release
GitHub.com
- Go to Tags page
- Click … and select create release on the tag
- Enter the version number in the release title
- Copy the changelog entry for this release into the
release notes
- Click publish release
Drupal.org
The builds on Drupal.org are incomplete as they don’t fully support Composer yet which is why we host a tarball on GitHub for those not using Composer.
- Drupal WxT Release Page
- Select the tag
- Copy/paste the following blurb into the release notes:
<strong>CHANGELOG</strong>
See the <a href="https://github.com/drupalwxt/wxt/blob/5.2.x/CHANGELOG.md">changelog.md</a> file.
6 - Theming
Largely when doing any theme related work with Drupal WxT this almost always should be done in a sub-theme.
For more on creating sub-themes please consult the official documentation:
To assist with sub-theme creation WxT Bootstrap provides an example starterkit that should be of benefit.
Note: Sub-themes are just like any other theme except they inherit the parent theme’s resources.
Sub Theme Configuration
a) Replace every instance of THEMENAME
with your chosen machine name often of the pattern <prefix>_bootstrap
.
b) Enable your new sub-theme preferably via drush:
drush en `<prefix>_bootstrap`
drush cc css-js
c) Point to your new sub theme for WxT Library to properly load assets under Themes Visibility on the /admin/config/wxt/wxt_library
page.
Notes
Inheriting Block Templates
If the theme you are extending has custom block templates these won’t be immediately inherited because a sub-theme creates copies of all the blocks in the parent theme and renames them with the sub-theme’s name as a prefix. Twig block templates are derived from the block’s name, so this breaks the link between these templates and their block.
Fixing this problem currently requires a hook in the THEMENAME.theme
file and should have the following contents:
/**
* Implements hook_theme_suggestions_HOOK_alter().
*/
function THEMENAME_theme_suggestions_block_alter(&$suggestions, $variables) {
// Load theme suggestions for blocks from parent theme.
// https://www.drupal.org/project/wxt/issues/3310485#comment-14715969
for ($i = 0; $i < count($suggestions); $i++) {
if (str_contains($suggestions[$i], 'THEMENAME_')) {
$new_suggestions = [
str_replace('THEMENAME_', '', $suggestions[$i]),
str_replace('THEMENAME_', 'wxt_bootstrap_', $suggestions[$i]),
];
array_splice($suggestions, $i, 0, $new_suggestions);
$i += 2;
}
}
}
Programmatic Logic
The following provides an example of how you can configure your sub theme to be installed as the default on a module install:
/**
* Implements hook_modules_installed().
*/
function MODULENAME_modules_installed($modules) {
if (in_array('wxt', $modules)) {
\Drupal::configFactory()
->getEditable('system.theme')
->set('default', 'THEMENAME')
->set('admin', 'claro')
->save(TRUE);
}
}
}
The following provides an example of how you can configure wxt_library
to use your sub theme by creating a config/install/wxt_library.settings.yml
file with the following contents:
url:
visibility: 0
pages:
- 'admin*'
- 'imagebrowser*'
- 'img_assist*'
- 'imce*'
- 'node/add/*'
- 'node/*/edit'
- 'print/*'
- 'printpdf/*'
- 'system/ajax'
- 'system/ajax/*'
theme:
visibility: 1
themes:
THEMENAME: THEMENAME
wxt_bootstrap: wxt_bootstrap
minimized:
options: 1
files:
types:
css: css
js: js
wxt:
theme: theme-gcweb
7 - Versioning
The Drupal WxT distribution is following semantic versioning.
WxT typically makes a sprint release every four to six weeks. We will also use sprint releases to package new minor releases of Drupal Core with WxT as they become available.
In addition, we will also increment the major version number of WxT about once every four to six months.
Extensions
Support for semantic versioning for extensions (modules, themes, etc) is still ongoing.
The three parts of our versioning system are MAJOR.FEATURE.SPRINT.
Given the following tag: 10.x-2.00:
10 | Major version of Drupal Core |
x | |
5 | Major version of WxT |
0 | Feature release of WxT. Also increments with minor core releases. |
0 | Sprint release between feature releases |
Note: Due to the constraints of drupal.org, there is no separator between the FEATURE and SPRINT digits.