16 Dec 2025
Drupal.org aggregator
Specbee: Drupal Paragraphs or Layout Builder? When to use what
Should we use Paragraphs or Layout Builder in Drupal? This guide breaks down the strengths of each approach and helps you choose the right module for flexible, editor-friendly page building.
16 Dec 2025 6:31am GMT
15 Dec 2025
Drupal.org aggregator
Drupal.org blog: GitLab CI: Drupal's strategy to empower a whole ecosystem
In this post, we share our CI strategy for all Drupal contributed modules. We believe that other large open-source projects may want to adopt or learn from the way we implemented a solution to centrally-manage CI while still allowing per-project customization.
How Drupal contributed modules do CI today?
Let's give some more details about how did we get here.
The past
In summer 2023, only 2 and a half years from today, we enabled the usage of GitLab CI for the Drupal ecosystem, which includes all contrib modules and Drupal core. We announced and promoted it at DrupalCon Lille 2023.
This new system would replace entirely the DrupalCI, the custom testing solution that Drupal core and projects used for nearly 10 years prior to enabling GitLab CI.
Core tests went from taking nearly 1h to taking 10 minutes. Adoption for contrib modules was as easy as adding a six-line file to their project.
The present
Core continued to evolve at its own pace, and the CI runs are now down to 5 minutes. They've been able to leverage concurrency, caching, and many other features available on GitLab CI.
Contrib modules also saw significant changes to improve their quality. Adoption was continuously growing, and the standard templates really took off, adding many new features.
As of today, we have more than 2000 contrib projects using GitLab CI.
Jobs
We offer, without writing a single line of code, the same type of tests and checks that core does.
These are: Composer lint, PHPCS, CSpell, PHPStan, ESLint, Stylelint, Nightwatch, PHPUnit, Test-only.

In addition to those, we also have: Upgrade status, Drupal CMS, GitLab Pages.
You can see that having things like "Upgrade status" or "Drupal CMS" compatibility are key for our contrib modules, and it's available out of the box.
Also, the GitLab Pages job allows for modules to publish a full documentation site based on their markdown files. If the files are there, the documentation site will be published. An example of this is our own documentation site for the shared CI templates: https://project.pages.drupalcode.org/gitlab_templates.
Most of these jobs will offer artifacts that can be downloaded by maintainers to fix the issues reported.
Customizations
Most of the above jobs can be disabled, if they are not wanted, with only a few lines of code (turn variables to 0).
We can also test multiple versions of Drupal, like the next or previous minors or majors, again with a few lines of code (turn variables to 1).
We achieved this by extending base jobs that can be configured via variables, like this:
composer:
extends: .composer-base
variables:
DRUPAL_CORE: $CORE_STABLE
IGNORE_PROJECT_DRUPAL_CORE_VERSION: 1
composer (max PHP version):
extends: .composer-base
rules:
- *opt-in-max-php-rule
- *check-max-php-version-rule
- when: always
variables:
PHP_VERSION: $CORE_PHP_MAX
DRUPAL_CORE: $CORE_STABLE
IGNORE_PROJECT_DRUPAL_CORE_VERSION: 1
composer (previous minor):
extends: .composer-base
rules:
- *opt-in-previous-minor-rule
- when: always
variables:
DRUPAL_CORE: $CORE_PREVIOUS_MINOR
IGNORE_PROJECT_DRUPAL_CORE_VERSION: 1
We always keep up with the latest core releases, so maintainers don't need to change anything to test the latest core versions. But if they want to "fix" the versions tested so these don't change, they can pin the version of the templates that they are using with just one line of code.
They can choose with PHP version or database engine to run tests with.
External integrations
The contrib templates can be used in external instances. This is actually a five-line file (similar to the one mentioned above), but the integration remains the same. We have several community members using the templates in their own GitLab instances with their own company projects, and everything works the same.
The future
Ever since we made the switch, we have positively shaped the contribution to Drupal. Module standards are very much aligned with core standards. We get really quick in-browser feedback about what to fix; we no longer need to upload extra (test-only) patches, etc.
The possibilities are endless, and we continue looking at the future as well. We are always open to hearing about improvements. For example, only recently, thanks to suggestions from the community, we added Drupal CMS compatibility check and support for recipes.
We are also checking if we can convert some of the jobs to reusable GitLab CI components (they weren't stable when we launched the templates).
All in all, the future looks bright, and we are really glad that we made this move as part of our broader GitLab initiative.
How other open source projects can adopt a similar solution (aka "implementation details")
Whether you have an open source project and want to do something similar, or you are just curious, here are some of the details about how we implemented this for the Drupal Ecosystem.
We had several goals in mind, some of them as must-haves, some of them as nice-to-haves. The must-haves were that it needed to be easy to adopt, and that it should allow the same functionality as the previous system. The nice-to-haves were that it would be easy to iterate and push changes to all projects using it, without project interaction, and that we could easily add new features and turn them on/off from a central place.
At the time, GitLab components were still in the works and didn't have a timeline to be stable, so we needed to think which other options were available. GitLab has the include functionality, that allows including external YAML files in a project's CI configuration. This was our starting point.
Template inheritance
We control the templates centrally at the GitLab Templates project. In there, you can see a folder called "includes", and those are the files that projects include. That's it! To make this easier, we provide a default template that gets prepopulated in GitLab and that containsthe right "includes" in the right places. The six-line template is here.
You can create a ".gitlab-ci.yml" file in the repo and add these:
include:
- project: $_GITLAB_TEMPLATES_REPO
ref: $_GITLAB_TEMPLATES_REF
file:
- '/includes/include.drupalci.main.yml'
- '/includes/include.drupalci.variables.yml'
- '/includes/include.drupalci.workflows.yml'
From that moment on, the project "inherits" all the templates (that we control centrally) and will start running the above CI jobs automatically.
You can see that there are three main files: one with variables, one with global workflow rules, and one containing all the jobs.
That is just the base. Each project can deviate, configure, or override any part of the template as desired, giving them flexibility that we might not be able to accommodate centrally.
We created extensive documentation and generated a GitLab Pages site to help with this: https://project.pages.drupalcode.org/gitlab_templates.
Should you want to include this in any other external GitLab instance, you just need to adapt the above to be fully qualified links as explained in our documentation page here.
As mentioned before, we can push a change (eg: bug fix, new feature) centrally, and as long as the projects make reference to our files, they will automatically receive the changes. This gives us great flexibility and extendibility, and best of all, maintainers don't need to worry about it as it is automatic for their projects.
We define variables that control the Drupal versions to tests against, the workflow rules that determine which jobs run and under which conditions, and most important of all, the logic for all the jobs ran in the pipelines.
We did it this way because it was the solution that would allow us to get all the must-haves and all the nice-to-haves. It allows literally thousands of projects to benefit instantly from shared CI checks and integration without barely writing code.
Versioning
We don't need a complex system for this, as the code is relatively small and straightforward compared to other projects, but we realised early that we needed a system because pushing the "latest" to everybody was risky, should a bug or unplanned issue arise.
We document our versioning system in the "Templates version" page. We use semver tagging, but we only maintain one branch. Depending on the changes introduced since the last tag, we increment X.Y.Z (X for breaking changes, Y for new features, Z for bug fixes), and we also generate a set of tags that will allow maintainers to pin specific versions, or moving-tags within the same major or minor. You can see the tagging script we use here.
Excerpt:
# Compute tags.
TAG="$1"
IFS=. read major minor micro <<<"${TAG}"
MINOR_TAG="${major}.${minor}.x-latest"
MAJOR_TAG="${major}.x-latest"
...
echo "Setting tag: $TAG"
git tag $TAG
git push origin $TAG
...
echo "Setting latest minor tag: $MINOR_TAG"
git tag -d $MINOR_TAG || TRUE
git push origin --delete $MINOR_TAG || TRUE
git tag $MINOR_TAG
git push origin $MINOR_TAG
...
echo "Setting latest major tag: $MAJOR_TAG"
git tag -d $MAJOR_TAG || TRUE
git push origin --delete $MAJOR_TAG || TRUE
git tag $MAJOR_TAG
git push origin $MAJOR_TAG
This process has been working well for us for around 2 years already.
Pushing changes to all contributed projects
Once the above versioning system was implemented, it was easier and quicker to iterate, and it also gave maintainers a chance to pin things. We normally push changes to the "main" branch, so all users wanting the latest changes can both benefit from them and also help us discover any possible regressions.
Once we are happy with the set of changes from the last tag, we can create new tags that maintainers can reference. Also, once we are happy that a tag is stable enough, we have a special tag named "default-ref" and all we need to do is change that tag to point to the specific stable version we want. Once we do it, that tag will automatically be pushed to all the contributed projects using the default setup.
The script that we use to set the default tag can be seen here.
Excerpt:
TAG="$1"
DEFAULT_TAG="default-ref"
echo "Setting default tag to be the same as: $TAG"
# Checkout the tag.
git checkout $TAG
# Override the default one.
git tag -d $DEFAULT_TAG || TRUE
git push origin --delete $DEFAULT_TAG || TRUE
git tag $DEFAULT_TAG
git push origin $DEFAULT_TAG
# Back to the main branch.
git checkout main
Implement it in your project
In the spirit of open source, we've documented the overarching strategy we used so that other teams fostering open source projects can adopt similar principles. We wanted to share how we did it, in case it helps any other project.
The key is to have a central place where you can control the default setup, and from there on, let projects decide what's best for their needs. They can stick to the default and recommended setup, but they could deviate from it should they need to.
15 Dec 2025 9:02pm GMT
Talking Drupal: Talking Drupal #532 - AI Marketing and Stuff
Today we are talking about AI Marketing,Marketing Trends, and The caber toss with guest Hayden Baillio. We'll also cover Drupal core 11.3 as our module of the week.
For show notes visit: https://www.talkingDrupal.com/532
Topics
- AI in Marketing: Hayden's Insights
- The Role of AI in Content Creation
- Challenges and Ethical Considerations of AI
- AI Training Data and Bias
- AI in Security Testing
- AI Replacing Jobs
- The Future of Marketing with AI
- Highland Games and Personal Hobbies
Resources
Guests
Hayden Baillio - hounder.co hgbaillio
Hosts
Nic Laflin - nLighteneddevelopment.com nicxvan John Picozzi - epam.com johnpicozzi Fei Lauren - feilauren
MOTW Correspondent
Martin Anderson-Clutz - mandclu.com mandclu
- Brief description:
- Have you been wanting a version of Drupal core that moves away from the hooks system, has PHP 8.5 support, or has better support for asynchronous queries? The newly released Drupal core 11.3 has all these and more.
- Module name/project name:
- Brief history
- Created in the last few days (hopefully) by the time this episode is released
- Changes
- Performance improvements
- New MYSQLi database driver. In combination with the PHP Fibers support added in Drupal 10.2, this should allow Drupal sites to run much faster. Not all hosting environments will have PHP configured to work with the new driver, so for now the new driver is in an experimental core module you will need to install to try the new driver
- Drupal can now lazy load multiple entities at a time using Fibers
- PHP 8.5 support should also improve performance, as will a number of caching improvements
- Some early testing in the community indicates some significant improvements for pages loaded from cold cache, anywhere from 30 to 40% fewer queries
- One of the significant changes in Drupal core 11.2 was the addition of HTMX as the intended successor to Drupal's older AJAX system. Drupal core 11.3 includes some significant steps on the path to replacing all the places that AJAX system in core
- There's a new HTMX factory object with methods to abstract the specifics of the attributes and headers needed to implement HTMX
- HTMX is now used for the Form Builder and ConfigSingleExportForm
- BigPipe no longer uses the older AJAX API, which itself uses jQuery
- New Workspace Provider concept, will be interesting to see what new possibilities this creates
- New administer node published status permission, previously required the much broader "administer nodes" permission
- Drupal core 11.3 also includes some capabilities that previously required contrib modules
- Links created within CKEditor5 now dynamically link to the entity and when rendered will automatically point to the most recent alias. Previously Drupal sites needed the Linkit module, which has been part of Drupal CMS since its release at the start of the year
- Drupal CMS is also heavily based on Drupal's recipe system, which includes the ability to automatically import content included within a recipe. Until now you still needed the default_content module to export content as YAML for inclusion in a recipe. With Drupal 11.3 you can export all entities of a particular type, optionally filtered by bundle, and optionally including all dependencies
- Many of Drupal's remaining hooks, particularly those for themes, now have OOP class replacements, so we're now very close to being able to deprecate .module and .theme files
- Listeners may remember that the Navigation module was added as an experimental module in Drupal core 10.3. In 11.3, the module is now officially stable, so the rethought admin menu that originally debuted as part of the Gin admin theme is now fully realized in Drupal core
- SDCs can now be marked to be excluded from the UI, for example if they are meant to only be nested within other components
- Drupal core 11.3 also introduces some new deprecations:
- Migrate Drupal and Migrate Drupal UI officially deprecated now that Drupal 7 is EOL
- Also field_layout, which was ultimately superseded by Layout Builder
- Promoted and Sticky fields are now hidden by default (an issue created more than 20 years ago! A five digit issue ID) - the user who created it had a drop.org username lol
- Another issue that sets the "Promoted" default value to FALSE for new content types was also resolved, but only 15 years old. It had a six-digit issue ID - barely!
- Theme engines have been deprecated!
- This may be the last feature release of Drupal core before version 12, which could drop as early as June 2026
- We'll include a link to the release highlights, but by the time you hear this there should also be an official announcement from Gabor and the DA with additional details
15 Dec 2025 7:00pm GMT