Releases: Joomla-Bible-Study/cwm-build-tools
v1.5.1 — opt-in verifyAssets check
Additive, opt-in, fully backward-compatible.
Added
-
cwm-buildasset-reference verification (build.verifyAssets). Withbuild.verifyAssets: trueincwm-build.config.json,cwm-buildfails — after the pre-build step, before zipping — if any file referenced by ajoomla.asset.json(type: script/stylewith a localuri) is absent from the source tree.This closes the silent-skip gap introduced alongside v1.5.0's ESM support: if a
*.es6.mjsmodule didn't compile (the JS build was skipped, or an older toolchain ignored the.es6.mjssuffix), the asset would 404 at runtime and any dependent JS would break for end users. Now it's a loud build failure with the offending file named.- Resolution is by basename under the manifest's media directory (a
urilikecom_proclaim/foo.min.jsis served from a media root that doesn't mirror the repo'smedia/js/layout). - CDN / absolute / protocol-relative URLs are skipped.
- Only
joomla.asset.jsonfiles that would actually ship (per the package's include/exclude rules) are checked. - Defaults to
false— existing consumers are unaffected until they opt in.
- Resolution is by basename under the manifest's media directory (a
Fixed (tests)
- Latent
fclose(STDIN)bug in the child-process gate test helper that surfaced under random execution order; the two gate tests now share arunBuildInChild()helper.
v1.5.0 — ESM rollup output + cwm-lint-deprecations
Additive, fully backward-compatible.
Added
-
ESM output from the shared Rollup config — unblocks first-class
JoomlaDialog.templates/rollup.config.jsnow picks output format by source suffix:*.es6.jsstill emits an IIFE bundle (every existing consumer builds exactly as before), while a new*.es6.mjssource emits an ES module (format: 'es'). In module builds, barejoomla.*specifiers (e.g.import JoomlaDialog from 'joomla.dialog') are marked external, so the import survives to the browser and Joomla's import-map resolves it at runtime. An IIFE bundle inlines everything and structurally cannot carry a live external ESM import — which is why consumingJoomlaDialog(or any Joomla JS module API) was previously impossible without hand-writing an unbundled module. Register the built file as atype="module"asset injoomla.asset.json. Extra externals (e.g.vue) via theMODULE_EXTERNALSenv var. -
cwm-lint-deprecations— flags Joomla 6/7 upgrade blockers in source. New CI-gating scanner (composer lint-deprecations) reportingfile:lineforbootstrap.modalassets,data-bs-toggle="modal"markup, legacy{handler: 'iframe'}modal links, the removedJoomla.ModalJS API, and jQuery globals. Exits non-zero on any finding (or--warnto report without failing). Skipsvendor/,node_modules/,build/,dist/,*.min.js.cwm-initnow offers to wire it into consumers.
Migration
No action required for existing builds (the old template ignores *.es6.mjs, so adopting it is safe to stage ahead of upgrading). To use JoomlaDialog: name the source <thing>.es6.mjs, import JoomlaDialog from 'joomla.dialog', and register the built <thing>.js as "type": "module". Note JoomlaDialog.confirm()/alert() are Promise-based, not synchronous like native confirm(). Run composer lint-deprecations to find Bootstrap-modal usages to migrate.
v1.4.1 — component media-link namespaced-source fix
Fixed
Component media now links from media/<name> when assets are namespaced. The dev linker always sourced a component's media from <root>/media, which only fits the <media folder="media"> layout (Proclaim). For the <media folder="media/com_x"> layout (e.g. com_cwmconnect, com_livingword), cwm-link pointed the install's media/<name> symlink one level too high — CSS/JS 404'd and cwm-verify flagged the correct symlink as a conflict. The resolver now prefers media/<name> when that subdir exists, else falls back to media/. Targets unchanged; only the source path is corrected.
Consumers on the namespaced layout should composer update cwm/build-tools to pick this up (within ^1.4).
Full PR: #27
v1.4.0 — flat-key build.properties (IDE-friendly)
Changed (non-breaking — both formats still parse)
-
build.propertiesswitched from INI sections to flat keys. Every
field is now a globally unique key (e.g.builder.j5.role,
paths.cwm/sibling) so Java-properties-aware IDEs (PhpStorm,
IntelliJ) don't flag "duplicate property key" warnings onrole,
path,db_host, etc. across sections. Same content, same semantics,
zero IDE noise.New canonical shape:
joomla.version = 5.4.2 builder.installs = j5, j6, j5-test builder.j5.role = dev builder.j5.path = /path/to/joomla5 builder.j5.url = https://j5-dev.local builder.j5.version = 5.4.2 builder.j5.db_host = localhost builder.j5.admin_email = admin@example.com # ... etc builder.j5-test.role = test builder.j5-test.path = /path/to/joomla5-test paths.joomla-bible-study/lib-cwmscripture = /Users/you/GitHub/lib_cwmscripture
-
Comment marker switched from
;to#to satisfy Java-properties
parsers too. PHP'sparse_ini_stringaccepts both via
PropertiesReader's pre-strip step.
Internal
PropertiesReader::fromLegacyFlat()renamed tofromFlat()(with the
old name retained as a private alias for any external caller still
using it). The "flat" format is the canonical v1.4 shape, not legacy.- Extended to honor explicit
builder.installs = ...lists. - Auto-discovers ids from
builder.<id>.pathkeys (preferred) before
falling back tobuilder.<id>.url(Proclaim legacy). - Parses
builder.<id>.role,builder.<id>.version, and modern
admin_user/pass/emailkey names alongside Proclaim's legacy
username/password/email.
- Extended to honor explicit
PropertiesReader::paths()now reads flatpaths.<package>keys
(preferred). Still falls back to the[paths]INI section for any
existing v1.2/v1.3 files.PropertiesReader::write()andwritePaths()now emit flat keys.PropertiesReader::fromSections()retained as a reader-only path for
backward compatibility with any developer's existing INI-style
build.propertiesfrom v1.0–v1.3. Nextcomposer setuprewrites
the file in the new flat shape.
Migration
Per consumer:
composer require --dev cwm/build-tools:^1.4composer cwm-sync-configsrefreshesbuild.dist.propertiesto
the new flat-key template.- Developers with an existing INI
build.propertieskeep working
thanks tofromSections()backward-compat. Runningcomposer setupwill migrate the file to the flat shape on next save.
v1.3.0 — manifest_cache staleness check + build.dist.properties sync
Added
cwm-verifydetects and rebuilds stalemanifest_cache. Each row in
#__extensionscarries amanifest_cacheJSON column that Joomla
populates on install and update. When an extension's install
scriptfileupdate()doesn't run cleanly, that column drifts away
from the source manifest XML — visible to users as
mb_strtolower(null)deprecation warnings in Joomla 6's manage view.ExtensionVerifier::parseManifestXml()reproduces Joomla's
Installer::parseXMLInstallFile()shape so cwm-verify can build
the canonical cache JSON.ExtensionVerifier::compareManifestCache()checks the four fields
the manage view actually reads (name,version,description,
author) — other fields are informational and not worth surfacing
as drift.cwm-verify --target testnow reportsSTALE: <name> manifest_cache — version: 'old' → 'new'per drifted extension.cwm-verify --target test --fixUPDATEs the row with rebuilt JSON
directly — no extension reinstall required.- Applies to self extensions AND CWM dep extensions installed via
path repositories (expectedFromPackages()resolves the source
manifest from each dep's joomlaLinks tuple).
cwm-sync-configsdistributesbuild.dist.propertiesto consumers.
Each consuming project gets an auto-managed copy of cwm-build-tools'
templates/build.properties.tmpl, headed by a marker comment telling
developers to edit the upstream template rather than the local file.
Schema changes tobuild.properties(therolefield added in
v1.1.0, the[paths]block added in v1.2.0) now propagate to every
consumer on the nextcomposer cwm-sync-configsrun rather than
silently drifting.
Migration
Per-repo:
composer require --dev cwm/build-tools:^1.3composer cwm-sync-configs— refreshesbuild.dist.properties
from the upstream template (committed change, review the diff).- Optionally
composer cwm-verify --target test --fixagainst a
live install with stale extension caches.
v1.2.2 — constraint satisfaction for stability qualifiers
Fixed
DevTargetVerifier::satisfies()now understands Composer stability
qualifiers. Constraints like@dev,*@dev, and bare*are stability
/ wildcard expressions, not semver ranges; the previous implementation
fell through to literal-match and reportedout of rangefor every
path-repo dep pinned with@dev(which is the common shape for CWM
sibling deps). Now treats*/@dev/*@devas any-version,
strips trailing@<stability>qualifiers from caret/tilde constraints,
and matchesdev-<branch>literally against the installed version.
v1.2.1 — honor config.vendor-dir
Fixed
InstalledPackageReadernow honorscomposer.json's
config.vendor-diroverride. Consumers with a non-default vendor
directory (e.g. CWMLivingWord and Proclaim both ship with
"vendor-dir": "libraries/vendor") had the cross-package machinery
silently degrade to zero deps —installed.jsonwas being looked up
at<project>/vendor/composer/installed.jsonregardless of the
configured location.cwm-linkandcwm-verify --target devnow
resolveinstalled.jsonagainst the actual vendor dir, so the CWM
dependencies section appears as designed.
v1.2.0 — two-level cwmSiblings config
Added
-
dev.cwmSiblingsdeclaration incwm-build.config.json— a project
now declares the list of CWM sibling packages it expects to find as
Composer path repositories:{ "dev": { "cwmSiblings": [ "joomla-bible-study/lib-cwmscripture", "cwm/scripture-links" ] } }This is the project-level "I depend on these siblings via local
checkouts" declaration — committed, shared across all developers. -
[paths]block inbuild.properties— per-developer mapping
from each declared CWM sibling to its absolute path on the local
machine:[paths] joomla-bible-study/lib-cwmscripture = /Users/brent/GitHub/lib_cwmscripture cwm/scripture-links = /Users/brent/GitHub/CWMScriptureLinks
Gitignored (per-developer). Companion to the existing per-install
configuration sections.PropertiesReader::paths(): array<string, string>reads the block.PropertiesReader::writePaths(array $paths)rewrites it while
preserving every install section.PropertiesReader::write()reciprocally preserves any existing
[paths]block when re-emitting installs — neither path can
accidentally drop the other's state.
-
cwm-setupCWM-siblings flow — after the Joomla install
configuration step, the wizard now:- Reads
dev.cwmSiblingsfromcwm-build.config.json. - For each sibling, surfaces the existing
[paths]entry,
or auto-detects a sibling checkout at../<name>next to
the project root. - Prompts to confirm or change the path per sibling.
- Writes the resulting paths to
build.properties [paths]. - Synchronises
composer.json'srepositories[]block so each
declared sibling has a matching{"type":"path","url":"..."}
entry pointing at the developer's local checkout.
Subsequent
composer install/composer updatepicks up the
configured paths automatically — no hand-editing ofcomposer.json. - Reads
Changed (non-breaking)
PropertiesReader::fromSections()now excludes[paths]from the
install auto-discovery branch (wheninstalls =is omitted), so
a paths-only or paths-plus-sections file parses correctly.
Migration
Consumers using v1.1.0 path repositories (e.g. CWMLivingWord) can
adopt the new schema by:
- Adding
dev.cwmSiblingstocwm-build.config.jsonlisting each
Composer package consumed via a local path repo. - Running
composer setup— the wizard prompts for each sibling's
local path, writes them tobuild.properties, and rewrites
composer.json'srepositories[]block accordingly. - Re-running
composer install.
v1.1.0 — cross-component link awareness + --target test
Added
- Cross-component link awareness via
extra.cwm-build-tools.joomlaLinks.
Each CWM Composer package can now declare its Joomla install footprint
in its owncomposer.json:Consumers'{ "extra": { "cwm-build-tools": { "joomlaLinks": [ { "type": "library", "name": "cwmscripturelinks" }, { "type": "plugin", "group": "content", "element": "cwmsl_autolink" }, { "type": "module", "name": "mod_cwm_widget", "client": "site" }, { "type": "component","name": "com_cwmthing" } ] } } }cwm-linkandcwm-verifydiscover these declarations via
vendor/composer/installed.jsonand operate on them automatically —
no per-consumer wiring required.- New
CWM\BuildTools\Config\InstalledPackageReaderparses
installed.jsondirectly (Laravel-style —\Composer\InstalledVersions
stripsextrafrom its accessors), surfacing each CWM dep's version,
install path, path-repo source path (if installed via a path
repository), and validated joomlaLinks tuples. - New value object
CWM\BuildTools\Config\CwmPackage.
- New
cwm-linkwalks CWM Composer deps in addition to the consumer's own
extensions. Per-dep links land at the same canonical Joomla paths
(libraries/<name>,plugins/<group>/<element>, etc.) — re-running
cwm-linkfrom a different consuming repo is idempotent on shared
targets.- Conflict detection: when a symlink already exists at the expected
target but points somewhere other than this run's source, the
conflict is reported and skipped (exit 1) rather than silently
overwritten. New--forceflag reinstates overwrite for the rare
case where a developer actually wants to replace someone else's
link. Linker::check()return shape gainsexistingRealpathfor ok/wrong/
broken statuses so callers can compare without re-reading the link.
- Conflict detection: when a symlink already exists at the expected
build.propertiesinstall role. Each install can now declare
role = dev(the default — symlink-style working install where
cwm-linkdeploys) orrole = test(artifact-style install for the
newcwm-install-zipcommand). Multiple installs may share a role
(e.g.j5andj6both as dev). Legacy flat Proclaim-style
build.propertiesfiles default every install torole = dev.- New
PropertiesReader::installsFor(string $role)filters by role. InstallConfiggains aroleconstructor argument and
ROLE_DEV/ROLE_TESTconstants.
- New
cwm-install-zipcommand. New companion tocwm-link: builds nothing
itself, but takes the most recent dist zip produced bycwm-build
(matched viabuild.outputGlob) and installs it into every Joomla
install withrole = testby invoking the bundled Joomla CLI:Re-running on an existing extension triggers Joomla's upgrade path —php <joomlaRoot>/cli/joomla.php extension:install --path=<zip>
install scriptfileupdate()runs, manifestupdate.sqlmigrations
apply. Use this to exercise the SHIPPED artifact end-to-end (install
scriptfile, dist exclusions, schema migrations) before a release,
separately from the symlink-style dev workflow.- Implementation: new
src/Dev/ExtensionInstaller.phpand
src/Dev/InstallResult.php; thin bin wrapper at
bin/cwm-install-zip. --zip <path>flag to override theoutputGlobresolution.proc_openis invoked array-form (no shell) per CLAUDE.md guardrails.
- Implementation: new
cwm-verify --target <role>flag. Filters which installs are
verified. Without--target, every install is verified per its
declared role.- For
role = devinstalls, the new
CWM\BuildTools\Dev\DevTargetVerifierchecks: every expected
symlink is in place (self + every CWM dep); each installed dep
version satisfies the constraint in the consuming project's
composer.json; each path-repo dep has a clean working tree
(git -C <source> status --porcelain). - For
role = testinstalls, the existingExtensionVerifiernow
also walks each CWM dep's declared joomlaLinks and confirms each
one is registered in#__extensionsat the right(type, element, folder, client_id)tuple. - New public method
ExtensionVerifier::expectedFromPackages(list<CwmPackage>): list<array>
folds dep declarations into the existing expected-extension shape. lookup()now filters byclient_idwhen supplied, so two modules
with the same element in different clients (site vs administrator)
resolve unambiguously.
- For
Changed (non-breaking)
Linker::check()return shape now includes anexistingRealpathkey
forok,wrong, andbrokenstatuses. Existing callers reading
onlystatusandmessageare unaffected.ExtensionVerifier::verify()signature gains an optional
array $packages = []parameter. Existing callers passing
(InstallConfig, bool)continue to work unchanged.templates/build.properties.tmpldocuments therolefield and ships
with a commented-out[j5-test]block for the new artifact-target
install.
Migration
Consumers pin to ^1.1. Per-repo migration:
composer require --dev cwm/build-tools:^1.1- Add
extra.cwm-build-tools.joomlaLinksto yourcomposer.json,
derived from yourmanifests.extensions[]in
cwm-build.config.json(one tuple per declared extension). - (Optional) Add a
[j5-test]block withrole = testto your
build.propertiesto enablecwm-install-zipand
cwm-verify --target test. - Re-run
composer cwm-link— output now shows the deps section;
existing links are reported as idempotent. - Re-run
composer cwm-verify— output now shows per-dep version,
link state, and (for path-repo deps) working-tree cleanliness.
v1.0.2
Added
versionTracking profiles — consumers can now declare "profile": "component" | "library" | "package-wrapper" in cwm-build.config.json instead of hand-authoring the entire versionTracking block.
- Profile defaults live in
templates/profiles/<name>.jsonand resolve throughCWM\BuildTools\Config\ProfileResolverat every read site (cwm-bump,cwm-release,substitute-tokens, andrelease.sh's gate check). - The consumer's
versionTrackingkey is still honored as an override layer: maps deep-merge, lists replace wholesale. cwm-initdetects the right profile fromextension.typeand pre-fills the prompt.cwm-sync-configsprints a migration hint when an inline block exists without a profile, and a "safe to delete" hint when an inline block exactly matches the profile defaults. Never auto-rewrites JSON.
Migration
Existing consumers can keep their inline versionTracking block (no breaking change) or migrate to the profile pattern:
{
"extension": { "type": "library", "name": "lib_x" },
"profile": "library"
// Optional: add "versionTracking": { ... } to override per-repo bits.
}