Lesson 7.4: A theme under Git, with a composer.json
A theme with a future lives in a repository of its own, whose root is the extension folder. That gives it its own
history, tags and releases, and lets any site install it with Composer. In this lesson learners turn training_theme
into such a repository and give it the composer.json that makes composer require put it in the right place.
At a glance
| Course | Theme design |
| Module | Module 7: Packaging a theme |
| Duration | 45 minutes (15 presenting, 30 exercise) |
| Level | Intermediate |
| Audience | Designers and developers who publish themes |
| Prerequisites | Lesson 7.1; Git installed and a Git name and e-mail set (any address at example.invalid for the course) |
Learning objectives
After this lesson, learners can:
- explain why a theme has a repository of its own rather than living in a site's repository;
- start the repository with a
.gitignorethat keeps editor leftovers, build output and secrets out; - write commit messages with the prefixes
Added:,Updated:,Removed:,Renamed:,Fixed:; - write a
composer.jsonwith the right type, license and extension folder name, and validate it.
Trainer's notes
How to present it
Sources: Putting a theme under Git, A .gitignore for themes and composer.json for a theme.
- One repository per theme (4 minutes). Its own history and releases; installable with Composer by any site; contributors see only the theme; it can be public while the site stays private.
- The .gitignore (2 minutes). The wizard wrote one: editor leftovers, operating system files,
/vendor/,/node_modules/, logs. - Commit messages (3 minutes). What changed for the user and why, with a prefix, so the changelog can be built
from the history. Example:
Fixed: The map popup escapes the address and the title. - composer.json (6 minutes). The fields that matter:
name(vendor/package, lower case),description,type: ezpublish-legacy-extension,licenseas an SPDX identifier equal toLICENSE,authors,homepage,support,require(PHP and the installer plugin), andextra.ezpublish-legacy-extension-name(the folder name belowextension/).versionis usually left out: Packagist reads it from the tag.
Common questions
Do I need GitHub for this lesson? No. The repository stays local; publishing is a decision for module 8 and for real themes.
Which vendor name? Your organization's Packagist vendor. For the course, a made-up lower-case name is fine.
Can I keep the theme inside the site's repository and still publish it later? You can split it out later, but it is easier to start separate.
Pitfalls
- Committing in the installation's repository.
cd extension/training_themefirst; check withgit rev-parse --show-toplevel. - A personal address in commits. Use a course address at
example.invalidin the lab. - A license that differs between
composer.jsonandLICENSE. Write the SPDX form of the same license (for GPL version 2 or later:GPL-2.0-or-later).
Hands-on exercise
On your own training installation. Nothing is pushed anywhere.
cd extension/training_themeand check that it is not already inside a repository:git rev-parse --show-toplevel(an error, or the installation's root, means it is not a repository of its own).Read the
.gitignorethe wizard wrote. Add a line*.ezpkgso exported packages are not committed by accident.Start the repository:
git init -b main.Set a course identity for this repository only:
git config user.name "Course Learner"andgit config user.email "[email protected]".Check what would be committed:
git status --short. Every file should be one you mean to publish.Commit:
git add . && git commit -m "Added: The training_theme theme extension".Write
composer.jsonat the theme's root:{ "name": "training/training_theme", "description": "Training theme built in the Theme design course", "type": "ezpublish-legacy-extension", "license": "GPL-2.0-or-later", "require": { "php": ">=8.0", "se7enxweb/ezpublish-legacy-installer": "*" }, "extra": { "ezpublish-legacy-extension-name": "training_theme" } }Change
licenseto the SPDX form of the license in yourLICENSEfile.If Composer is available, run
composer validatein the theme folder and read the result.Commit it:
git add composer.json && git commit -m "Added: composer.json so the theme can be required by name".Read the history:
git log --oneline.
Expected result
- Step 5 lists only the theme's own files (no
.bak, no.ezpkg, no editor files). - Step 8:
composer validatereports the file valid; warnings about missing optional fields (such asauthors) are read and noted. - Step 10 shows two commits with prefixed messages.
- The trainer checks that the commits are in
extension/training_theme/.git, not in the installation's repository.
Quiz
- Give two reasons for a repository per theme.
- Which
composer.jsonfield makes the installer place the package belowextension/? A.nameB.type: ezpublish-legacy-extensionC.homepageD.version - Which field sets the folder name below
extension/? - Why is
versionusually left out of a theme'scomposer.json? - Write a commit message for a change that makes the breadcrumb wrap on phones.
Answers
- Any two: its own history and releases, installable with Composer by any site, contributors see only the theme, it can be public while the site stays private (Putting a theme under Git).
- B.
extra.ezpublish-legacy-extension-name.- Packagist reads the version from the Git tag; a field in the file would have to be kept in sync by hand.
- For example
Updated: The breadcrumb wraps on narrow screens instead of overflowing.
Further reading
- From the wizard's output to the first commit.
- The Composer installer and the folder name.
- Testing a Composer install before publishing.
- Develop with Git: working on extensions inside an installation.
Related pages
- Module 7: Packaging a theme
- Theme design: course overview
- Trainers: start here
- Free documentation: this page is free to copy, translate and improve under the GNU FDL 1.3.
Become an Exponential Partner
Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.