0.22.0
Released 2026-10-27
Added
composer <name>through the shim runs the rootcomposer.jsonscript of that name withviv run <name>, as Composer does when no Composer command has that name. The shim used to pass it to a real Composer, and failed where none is installed (#379)- A native adapter for
automattic/jetpack-autoloader:viv installon a project that requires it runs without--no-plugins, and writesvendor/jetpack-autoloader/andvendor/autoload_packages.phpbyte for byte as Composer 2.10.2 does forinstallandinstall -o(#389) viv installin a project with acomposer.jsonand nocomposer.lockprints Composer's "No composer.lock file present" warning, resolves, writescomposer.lockand installs, as Composer does, instead of stopping. Only the flagsupdateshares withinstallcarry over;-o,-a,--apcu-autoloaderand--adopttake their defaults (#362)viv dump-autoloadworks without acomposer.lock, reading the package list fromvendor/composer/installed.jsonas Composer does, and leavesinstalled.jsonuntouched in that case.--devforces dev mode
Changed
viv dump-autoloadwithout--devor--no-devfollows thedevkey ofvendor/composer/installed.json, as Composer does, so avendor/installed with--no-devdumps without its dev packages. It used to dump in dev mode every timeviv --help, the man pages and the crate description call viv a package manager that installs fromcomposer.lock, resolving it first when it is missing, instead of sayinginstallworks only from the lock (#366)
Fixed
viv updateranks a numbered pre-release such as4.4.0-RC1below4.4.0, as Composer does, sominimum-stability: devlocks the stable release. Thesemver-php0.1.0 crate ranked4.4.0-RC1above4.4.0. viv now carries a fixed copy of that crate insrc/semver/php/, so the build published to crates.io has the fix too (#386)viv update --lockandviv validate --fixwrite the lock'sminimum-stability,stability-flagsandprefer-stablefrom the currentcomposer.json, as Composer does, andupdate --lockwritesprefer-lowestfrom its own--prefer-lowestflag instead of keeping the old lock's value (validate --fixkeeps it, as it has no such flag). They used to copy all three from the old lock, so a changedminimum-stability, a newprefer-stableor a require with an@betasuffix stayed out of the lock (#390)viv update --lockandviv validate --fixwrite the lock'splatform,platform-devandplatform-overridesfrom the currentcomposer.json, as Composer does. They used to refresh onlycontent-hash, so a changedphpconstraint or a newconfig.platformentry stayed out of the lock (#380)viv installextracts a zip dist that is a local file: adist.urlthat is a path relative to the project root (./private/plugin.zip), an absolute path or afile://URL. It used to stop withinvalid dist URL ...: relative URL without a basefor an inlinepackagerepository, which Composer installs. viv reads the file again on every install instead of keeping a copy in the cache by URL, so a zip replaced under the same URL is not served stale, and it checksdist.shasumwhen the lock has one (#388)--link-modeworks on every command that links packages intovendor/:update,add,rm,new,init,workspace init,workspace addandisolate. The warning "Pass --link-mode copy to silence this warning" names a flag that onlyviv installaccepted, so the other commands answered it withunexpected argument '--link-mode'.viv install --link-mode copyon a project with nocomposer.locknow passes the flag on to theupdateit falls back to (#387)viv updatefinds a version newly published to acomposerrepository that serves one fullpackages.jsonand no/p2/endpoints, as Satis and SatisPress do. viv used to keep that file for ten minutes without a request, so an update inside the window kept the old list. It now revalidates the file on every update with a conditional request, or downloads it again when the server sends noLast-Modified. Repositories with/p2/or provider endpoints, such as packagist.org and wpackagist, keep the ten-minute window, and--metadata-ttlstill sets a window for the full file (#377)viv updatekeeps one metadata cache percomposerrepository URL, as Composer does. Two repositories on the same host, with another path, port or scheme, used to share onepackages.jsonand its provider files, so one repository could read the other's cached package list and send the other'sLast-Modifiedin its conditional request. The first run after upgrading downloads each repository's metadata once, because the cache moves fromrepo-v0/torepo-v1/;viv cache pruneremoves the old directory (#378)viv install,viv update,viv require,viv remove,viv newandviv diagnoseread the PHP extensions from the PHP build thatconfig.platform.phppins, the same buildviv runuses, instead of thephponPATH. A project that pins PHP 8.4 no longer fails withext-dommissing when nophpis onPATH. These commands never install the pinned build: when it is not installed, orconfig.platform.phpis a version no build exists for, they use thephponPATH, as for a project with no pin.viv lock mergere-solves against the samephp. When one of these commands refuses aphp,ext-*orlib-*requirement, the error now ends with a line naming thephpit read the platform packages from, and whenconfig.platform.phppins a build that is not installed it says how to install it (#385)viv requirewrites the originalcomposer.jsonandcomposer.lockback when the resolution fails, and prints Composer's "Installation failed, reverting ..." line. It used to leavecomposer.jsonholding the new requirement, reindented and with its keys reordered.viv rmwritescomposer.jsonback when its update fails before the lock is written, and prints "Removal failed, reverting ./composer.json to its original content." Both revert only until the lock is written, as Composer does (#288)viv cache prunekeeps thegit-v0bucket of git source mirrors. It deleted the mirrors ofsource.type: gitpackages, so the next install cloned each repository again (#360)- A partial update that needs a newer version of a package held at its locked version says the package is fixed to that version by the partial update and, unless
-Wwas passed, points to--with-all-dependencies. It used to say the package "could not be found in any version" (#363) - A package from a VCS repository gets Composer's lock entry:
licenseand ascriptslistener are one-item arrays, and a GitHub repository with issues enabled getssupport.issues(#374) installed.json,installed.php,platform_check.phpand.vivace-stateinvendor/composer/, and thecomposer.jsonthatviv normalizerewrites, are written with the umask mode instead of0600, so PHP running as another user can read them. A rewrite keeps the mode of the file it replaces, as Composer's does (#375)