PHPackages                             webhubworks/package-updater - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. webhubworks/package-updater

ActiveProject[Framework](/categories/framework)

webhubworks/package-updater
===========================

Bulk-update Composer packages (and Craft / Craft plugins) across all your local repos.

1.0.29(1w ago)072↓50%MITPHPPHP ^8.2

Since May 15Pushed 1w agoCompare

[ Source](https://github.com/webhubworks/package-updater)[ Packagist](https://packagist.org/packages/webhubworks/package-updater)[ Docs](https://webhub.de)[ RSS](/packages/webhubworks-package-updater/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (4)Versions (31)Used By (0)

package-updater
===============

[](#package-updater)

A Laravel Zero CLI for bulk-updating Composer packages — and Craft itself / Craft plugins — across every repo on your machine. Runs `git pull` on the appropriate long-lived branch (`develop` → `dev` → `staging` → `stag` → `stage` → `main` → `master` → `prod` → `live`), then the package update, optional `composer prep`, optional `site-crawler`. Skips dirty repos, captures full per-repo transcripts, summarises everything, and offers to open repos with uncommitted changes in GitKraken for review.

1. Installation
---------------

[](#1-installation)

### Global (recommended)

[](#global-recommended)

```
composer global require webhubworks/package-updater
```

Make sure Composer's global bin directory is on your `PATH` — typically `~/.composer/vendor/bin` (macOS/Linux) or `%APPDATA%\Composer\vendor\bin`(Windows). After that, both `package-updater` and the shorter alias `pu`are available from anywhere.

Set `REPOS_DIR` in your shell (or via `.env` next to the tool) so it points at the directory you want scanned (defaults to `$HOME/reps`). The walker descends through grouping folders (e.g. `~/reps/my7steps/`), considers a directory a candidate only when it contains a `composer.lock`, and filters out anything that's not a git repo or that itself declares `"type": "craft-plugin"`.

To upgrade later:

```
composer global update webhubworks/package-updater
```

### Local clone (for development)

[](#local-clone-for-development)

```
cd ~/reps
git clone  package-updater
cd package-updater
composer install
./package-updater list
```

2. Commands
-----------

[](#2-commands)

Run `pu ` (or the longer `package-updater `). Bare `pu`prints the command list. Every command is fully interactive — it will prompt for any answer it needs.

### `update`

[](#update)

Single-repo helper. Run it from inside a repo: it verifies the working tree is clean, runs `composer update` (auto-detecting ddev when `.ddev/config.yaml` exists), parses Upgrading / Downgrading / Installing / Removing lines from composer's output, and commits the result with title `Package updates` and a body listing each change. Optional package arguments (`pu update vendor/foo vendor/bar`) restrict the update to those packages. Use `--no-ddev` to force host composer, `--commit` / `--no-commit` to skip the commit prompt.

### `update:all`

[](#updateall)

Universal bulk update — one composer package across every local repo that depends on it. Pick a `vendor/name`, pick a target version, pick which of the matching repos to run on, pick parallelism, confirm. Each repo runs `git pull` → `ddev start` → `ddev composer update` → `composer prep` (if defined) → `ddev stop` (optional). Repos already at the target version are pre-skipped; with a bare target version, repos on a different major are pre-skipped too (prefix the version with `!`to force across majors).

`--filter-name=` narrows the match set to repos whose `composer.json` `name` contains the given substring — e.g. `pu update:all vendor/foo --filter-name=mvb` only considers repos with `mvb` in their composer name.

The package argument also accepts a wildcard — e.g. `pu update:all 'laravel-lang/*'` matches every locked package under that vendor prefix and runs `composer update laravel-lang/* -W` per repo. Wildcards skip the target-version and transitive-parent prompts and always pass `-W` (composer almost always needs it to bump siblings together). Quote the pattern in your shell so it isn't glob-expanded.

### `remove`

[](#remove)

Bulk counterpart for removing a package. Same scan as `update:all`, including wildcard support (`pu remove 'laravel-lang/*'`). Pre-skips repos where the package is only a transitive dependency (composer remove only works on declared deps) and pre-skips dirty repos. For each remaining repo it detects whether the package is in `require`or `require-dev` and groups the removals so composer is called with `--dev` for the dev-only ones (plain for the rest). On success it commits the change with title `Remove ` (or `Remove N packages` for wildcards) and a body listing the removed names.

```
pu remove vendor/foo                   # exact name
pu remove 'laravel-lang/*' --dry-run   # preview which packages would be removed
```

### `update:craft`

[](#updatecraft)

Craft-aware variant. Same flow, but:

- Identifies repos by Craft plugin handle (or the literal `craft` to match every site with `craftcms/cms` in `composer.json`).
- After `ddev start`, always syncs the working copy before the update: `ddev composer install` → `ddev php craft migrate/all` → `ddev php craft project-config/apply`.
- Runs `ddev php craft update ` (with sensible defaults you can edit at the prompt) instead of `composer update`.
- After `composer prep`, optionally runs `site-crawler crawl:ddev` in a second multiselect-chosen subset of repos. Parses the crawler's "Failed requests" table and warns on any 5xx URLs even if the crawler itself exited cleanly.

`--filter-name=` works the same way as on `update:all`: restricts the match set to repos whose `composer.json` `name` contains the substring.

### `retry`

[](#retry)

Re-runs the most recent `update:all` or `update:craft` non-interactively using the answers persisted in `logs/last-run.json`. Useful for working through a batch in chunks — already-up-to-date repos are skipped by the target-version filter, so each retry picks up where the previous one left off.

### `open`

[](#open)

Opens repos from the most recent run in GitKraken (one tab per repo via the `gitkraken://` URL scheme). By default it surfaces repos that warrant review: uncommitted working-tree changes, failed steps, failing tests after `composer prep`, crawler failures, or 5xx URLs from the crawler. You can narrow the pool with `--filter=changed`, `--filter=failed`, or `--filter=all`. Both `update:all` and `update:craft` also offer this prompt directly at the end of a run — push the resulting commits via GitKraken without context-switching.

### Logs and transcripts

[](#logs-and-transcripts)

- `logs/transcripts/-.log` — full output of every step for one repo, from `git status` to `ddev stop`. Always written, success or fail.
- `logs/--.log` — narrow per-step log written when a specific command fails or its tests don't pass.
- `logs/last-run.json` — the resolved command + arguments + options of the last run (powers `retry`).
- `logs/last-results.json` — per-repo results of the last run (powers `open`).

In sequential mode (`--parallel=1`) every step's output streams live to the terminal. Parallel mode does not stream (output would interleave) — the transcript and per-step logs are how you investigate.

3. Configuration
----------------

[](#3-configuration)

### Repo directory

[](#repo-directory)

`REPOS_DIR` (env var, or `.env` next to the tool when running from a local clone) — single source of truth for where to scan. Default: `$HOME/reps`. Per-run override available on every command via `--reps-dir=`.

### Git credentials (HTTPS remotes)

[](#git-credentials-https-remotes)

The tool shells out to `git`, which uses your CLI credentials — not GitKraken's. For HTTPS GitHub remotes, install and configure the GitHub CLI once:

```
brew install gh
gh auth login          # pick HTTPS, browser auth
gh auth setup-git      # register gh as git's credential helper
```

For Bitbucket / GitLab / Azure DevOps HTTPS remotes, use Git Credential Manager:

```
brew install --cask git-credential-manager
git-credential-manager configure
```

After either, `git pull` runs without prompting on matching remotes.

### Host SSH agent (SSH remotes)

[](#host-ssh-agent-ssh-remotes)

Repos cloned over SSH (`git@bitbucket.org:…`, `git@github.com:…`) bypass those credential helpers and need your host's SSH agent to have the right key loaded. Add a per-host block to `~/.ssh/config` so macOS loads it on login:

```
Host bitbucket.org
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_rsa

```

Then once:

```
ssh-add --apple-use-keychain ~/.ssh/id_rsa
```

To find which local key matches the fingerprint shown in your git host's UI:

```
for f in ~/.ssh/*.pub; do ssh-keygen -lf "$f"; done
```

### DDEV SSH for private composer dependencies

[](#ddev-ssh-for-private-composer-dependencies)

`composer update` runs inside ddev, which uses a global, shared SSH-agent container. The tool runs `ddev auth ssh` for you once at the start of a real run (after the confirm prompt, before any repo work) so private GitHub composer sources resolve without per-repo setup. If your SSH keys have passphrases, you'll be prompted at that point. The step can be skipped if you've already loaded keys in this shell.

### DDEV hostnames / sudo

[](#ddev-hostnames--sudo)

The first time ddev starts a given project on this device it may need sudo to add the project's `.ddev.site` hostname to `/etc/hosts`. In a piped/parallel run that would hang forever on the password prompt — the tool watches for the trigger lines (`needs to run with administrative privileges` / `may need to enter your password for sudo`) and kills ddev immediately, then fails that repo with a hint telling you to run `ddev start` manually once and then `retry`.

### GitKraken (for the open feature)

[](#gitkraken-for-the-open-feature)

Uses the `gitkraken://repo/path/` URL scheme via macOS `open`. No extra setup beyond installing the GitKraken app.

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance98

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

30

Last Release

11d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/53560154?v=4)[webhub](/maintainers/webhubworks)[@webhubworks](https://github.com/webhubworks)

![](https://www.gravatar.com/avatar/2c4c102b81ef27c8c29f2a30fff2a9c86619d4c05f9128c5aa97bb94c991709a?d=identicon)[marventhieme](/maintainers/marventhieme)

---

Top Contributors

[![marventhieme](https://avatars.githubusercontent.com/u/53627227?v=4)](https://github.com/marventhieme "marventhieme (70 commits)")

---

Tags

composercliconsoleframeworklaravellaravel-zeroCraft

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/webhubworks-package-updater/health.svg)

```
[![Health](https://phpackages.com/badges/webhubworks-package-updater/health.svg)](https://phpackages.com/packages/webhubworks-package-updater)
```

###  Alternatives

[laravel-zero/laravel-zero

The Laravel Zero Framework.

4.0k43.9k3](/packages/laravel-zero-laravel-zero)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
