Update your extension
Follow this page to merge a new Eddy release into your extension, with Claude Code or by hand. A release brings changes such as new CI jobs, tooling releases and fixes to the build and to the docs it generates. The merge keeps your own code and your own changes to the template. The last section lists what stays current between updates.
An extension created from a 4.x release needs a one-off upgrade to 5.x first. From 4.15.0, its AGENTS.md fetches the update skill from a URL that returns 404, and earlier releases have no update skill. The upgrade also replaces .devtools/ with the tooling package and CircleCI with GitHub Actions. For the steps, see Upgrade a 4.x project to 5.x.
Update with Claude Code
If you use Claude Code, ask Claude to "update scaffold" in your project. Claude follows the instructions in your project's AGENTS.md: it fetches the update-consumer-eddy skill with these commands, then runs it:
mkdir -p .claude/skills/update-consumer-eddy
curl -fsSL -o .claude/skills/update-consumer-eddy/SKILL.md \
https://raw.githubusercontent.com/drevops/eddy/main/.eddy/skills/update-consumer-eddy/SKILL.md
The skill installs 2 releases in scratch directories: the release your project is on and the release it updates to. Both use the installer answers of your project. Then it merges the difference between them into your project:
Only a line that both you and the release changed needs a decision. On such a line, the skill keeps your change and applies the release's change next to it. Then it builds, lints and tests the result, and opens a pull request that lists the conflicts it resolved by judgment. In a project without a command wrapper, the pull request's CI runs the lint and the tests instead.
The merge skips your extension's own code, the paths that step 4 of Update by hand lists. The skill changes these files on top of the merge:
- It takes
scripts/eddy-toolingfrom the release, even when you changed it. The pull request lists your change as one that belongs in a patch for the package. - It removes a template file that the release removed, even when you changed it, and lists it in the pull request.
- It keeps your
package-lock.json, and regenerates it when the mergedpackage.jsonchanged. - It finds references to example files that your project doesn't have, in the docs and configurations. It points each one at your own file or removes it.
- For a project from 4.x, it removes
.devtools/,.circleci/and the Cloudflare tunnel hooks.
The skill decides these from what it finds:
- Which release. Without a tag, the skill reads the 10 most recently published releases on GitHub. It skips drafts and the releases marked as pre-releases, then updates to the highest version left. To update to a particular release, name its tag:
/update-consumer-eddy <tag>. When your project is already on that release or a newer one, the skill stops, unless you add or drop a Drupal major. - Which release you're on. The skill reads it from
extra.eddy.versionincomposer.dev.json, or from your last scaffold update commit. The installer records that value when you install from a GitHub archive, and every update records it again. An extension created from a branch download records the commit it came from, such as5.0.0-3-g1a2b3c4. The skill then renders that commit in place of a release. Without a recorded release, or with one older than 4.14.0, you decide on every difference between your files and the new release. - Adding or dropping a Drupal major. Ask for it in the same request, such as
/update-consumer-eddy add Drupal 12. The skill changes the major's CI jobs and badges. For a new major, it also makes the code changes the major needs, each in a commit of its own. It does this even when there's no newer release to update to. - Permissions. On its first run, the skill might add a few command permissions to
.claude/settings.local.jsonand ask you to restart Claude Code. Run it again after the restart.
Update by hand
To update without Claude Code:
- Read the notes of every release after the one your project is on, which
extra.eddy.versionincomposer.dev.jsonnames, on the releases page. Look for breaking changes. - In a new, empty directory, run the installer with the answers your project uses. That directory isn't a clone of your repository, so pass your workflow branches and your GitHub repository, such as
INSTALL_BRANCHES=1.x,2.x INSTALL_GITHUB_REPOSITORY=owner/myextension php install.php. The installer installs the release shown in this site's footer. To update to another release or to the current code, install from a download instead. Your project shows its answers in these places:- The name and the type in the
nameandtypeof its.info.yml, and the machine name in that file's name - The command wrappers in which of
.ahoy.ymlandMakefileit has - The Drupal majors in the
drupal-versionvalues of.github/workflows/test.yml - The tools in their configuration files, such as
phpcs.xmlfor PHPCS andrenovate.jsonfor Renovate
- The name and the type in the
- Create a new branch in your extension's repository.
- Copy the new files into your extension's directory, apart from your extension's own code:
src,tests,config,css,js,composer.json,logo.png, the files named after your machine name andscripts/*-example.sh. The new files hold the example extension under your machine name, so copying those would overwrite your code. - Remove the template files that the new release dropped:
- Install from a download of the release your project is on into another new directory, with the same answers.
- Find each file that this directory holds and the directory from step 2 lacks. Remove it from your project, apart from the paths that step 4 leaves out.
- Resolve the differences between the new files and yours:
- Unless you passed them in step 2, keep your own branches in the workflow branch filters and your repository in the README badges.
- Keep the
extra.eddy.versionof the newcomposer.dev.json, since the next update reads the release you're on from it. - Take
scripts/eddy-toolingas the release ships it, and drop any change you made to it. To change what a tooling command does, patch the package instead.
- When your project has a
package-lock.json, restore your own copy withgit checkout HEAD -- package-lock.json. - If
package.jsonchanged, runnpm install --package-lock-onlyto bring the lock file in line with it. - Build the site.
- Check coding standards.
- Run the tests.
- Commit and push to your GitHub repository. Confirm that every CI job passes.
- Merge the new branch into your main branch. Confirm that the deployment job works.
To add or drop a Drupal major by hand:
- Select the majors you want when you run the installer in step 2 of the update.
- To add a major, add it to the
core_version_requirementof every*.info.ymlyour project has, because step 4 of the update leaves those files out. To drop a major from CI, leavecore_version_requirementas it is, as the update skill does. Remove a major from it only when your extension stops supporting that major. - For Drupal 12, also make the other code changes that Drupal 12 lists.
What else stays current
Between scaffold updates, these parts get updates of their own:
- The tooling package: CI installs its newest patch release within the
~constraint incomposer.dev.jsonon every run. A local checkout installs that release afterreset. A scaffold update raises the constraint for a new minor version, or when the new scaffold needs a newer release. The next run ofscripts/eddy-toolingthen installs a release that the new constraint allows. - When you keep Renovate, it updates the GitHub Actions and Docker images, and the npm packages within their major versions (see Dependency updates).