{"id":50496027,"url":"https://github.com/johnnyshankman/batch-upgrade-npm-packages","last_synced_at":"2026-06-02T07:03:04.989Z","repository":{"id":283335464,"uuid":"951435281","full_name":"johnnyshankman/batch-upgrade-npm-packages","owner":"johnnyshankman","description":"A command-line tool to upgrade npm packages across multiple repositories with automatic PR creation.","archived":false,"fork":false,"pushed_at":"2026-05-11T22:17:08.000Z","size":155,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-11T22:24:41.092Z","etag":null,"topics":["git","github","npm","package-management"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johnnyshankman.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-03-19T17:12:03.000Z","updated_at":"2026-05-11T22:10:47.000Z","dependencies_parsed_at":"2025-03-19T18:40:34.740Z","dependency_job_id":null,"html_url":"https://github.com/johnnyshankman/batch-upgrade-npm-packages","commit_stats":null,"previous_names":["johnnyshankman/batch-upgrade-npm-packages"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/johnnyshankman/batch-upgrade-npm-packages","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnnyshankman%2Fbatch-upgrade-npm-packages","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnnyshankman%2Fbatch-upgrade-npm-packages/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnnyshankman%2Fbatch-upgrade-npm-packages/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnnyshankman%2Fbatch-upgrade-npm-packages/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnnyshankman","download_url":"https://codeload.github.com/johnnyshankman/batch-upgrade-npm-packages/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnnyshankman%2Fbatch-upgrade-npm-packages/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33810343,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git","github","npm","package-management"],"created_at":"2026-06-02T07:03:02.985Z","updated_at":"2026-06-02T07:03:04.983Z","avatar_url":"https://github.com/johnnyshankman.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Batch Upgrade NPM Packages\n\nA command-line tool to upgrade npm packages across multiple repositories with automated pull request creation.\n\n[![npm version](https://img.shields.io/npm/v/batch-upgrade-npm-packages.svg)](https://www.npmjs.com/package/batch-upgrade-npm-packages)\n[![build](https://github.com/johnnyshankman/batch-upgrade-npm-packages/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/johnnyshankman/batch-upgrade-npm-packages/actions/workflows/build.yml)\n\n## Features\n\n- Update npm packages across multiple repositories in one command\n- Creates timestamped feature branches and opens PRs via the GitHub CLI for each repo; never commits to main\n- Skips packages that are already up-to-date (semver-aware)\n- Searches `dependencies`, `devDependencies`, and `peerDependencies`\n- Per-repo failures don't stop the run; the summary tells you what succeeded\n- Safe by default: refuses to overwrite uncommitted changes unless you explicitly opt in\n- Auto-detects each repo's base branch (`main`, `master`, or whatever `origin/HEAD` points at) — no hardcoding\n- `--dry-run` previews every change without touching any repo or remote\n- `--json` emits a machine-readable summary for scripting and CI\n- Subcommand-based CLI (`upgrade`, `config`, `completion`) familiar to users of `gh`/`docker`/`kubectl`\n- Documented exit codes; respects `NO_COLOR`, `CI`, and `BATCH_UPGRADE_*` env vars\n- Shell completion for bash, zsh, and fish\n\n## Requirements\n\n- Node.js 22 or higher\n- Git\n- [GitHub CLI](https://cli.github.com/) (`gh`), authenticated with `gh auth login`\n- npm\n\nThe package ships as ESM with bundled TypeScript declarations — no extra\n`@types/*` install is needed if you're using the library API from\nTypeScript.\n\n## Installation\n\n```bash\nnpm install -g batch-upgrade-npm-packages\ngh auth login   # one-time\n```\n\n## Quick start\n\n```bash\n# Update one package across three repos\nbatch-upgrade-npm upgrade -p react --versions ^18.0.0 -r ./web ./admin ./mobile\n\n# Preview only — no commits, no PRs, no fs changes\nbatch-upgrade-npm upgrade -p react --versions ^18.0.0 -r ./web --dry-run\n\n# JSON summary for CI\nCI=true batch-upgrade-npm upgrade --json -p react --versions ^18.0.0 -r ./app \u003e result.json\n```\n\n## Commands\n\n### `upgrade`\n\nUpdate packages across one or more repositories and open PRs.\n\n| Flag                           | Description                                                               |\n| ------------------------------ | ------------------------------------------------------------------------- |\n| `-p, --packages \u003cpackages...\u003e` | Packages to update (space-separated)                                      |\n| `--versions \u003cversions...\u003e`     | Version ranges in the same order as `--packages`                          |\n| `-r, --repos \u003crepos...\u003e`       | Repository paths                                                          |\n| `-b, --base \u003cbranch\u003e`          | Base branch override (auto-detected from `origin/HEAD` if omitted)        |\n| `-i, --interactive`            | Prompt for any missing input                                              |\n| `-y, --yes`                    | Skip the confirmation prompt (also implied by `CI=true` or non-TTY stdin) |\n| `--reset-hard`                 | Discard uncommitted changes in target repos before updating (DESTRUCTIVE) |\n| `-n, --dry-run`                | Preview without modifying any repository                                  |\n| `--json`                       | Emit a machine-readable JSON summary on stdout                            |\n\n### `config`\n\nInspect effective configuration resolved from environment variables.\n\n```bash\nbatch-upgrade-npm config list\nbatch-upgrade-npm config get packages\nbatch-upgrade-npm config set repos \"./a ./b\"\n```\n\n\u003e **Note:** `config set` is informational only. It does **not** write a config file or persist anything. It prints the equivalent `export …` line for you to copy into your shell profile. Persistent config files are not yet supported — use the environment variables documented below.\n\n### `completion`\n\nPrint a shell completion script.\n\n```bash\n# Bash\neval \"$(batch-upgrade-npm completion bash)\"\n\n# Zsh\nbatch-upgrade-npm completion zsh \u003e \"${fpath[1]}/_batch-upgrade-npm\"\n\n# Fish\nbatch-upgrade-npm completion fish \u003e ~/.config/fish/completions/batch-upgrade-npm.fish\n```\n\n## Global flags\n\n| Flag            | Description                                                      |\n| --------------- | ---------------------------------------------------------------- |\n| `-V, --version` | Print version                                                    |\n| `-h, --help`    | Print help (use `\u003ccommand\u003e --help` for command-specific options) |\n| `-q, --quiet`   | Suppress non-error output                                        |\n| `-v, --verbose` | Verbose output (includes child-process output)                   |\n| `--debug`       | Debug output (alias for `--verbose` with extra detail)           |\n| `--no-color`    | Disable colorized output (also honors `NO_COLOR=1`)              |\n\n`--quiet` is mutually exclusive with `--verbose` and `--debug`.\n\n## Environment variables\n\n| Var                         | Effect                                               |\n| --------------------------- | ---------------------------------------------------- |\n| `NO_COLOR=1`                | Disable colored output                               |\n| `CI=true`                   | Auto-confirm (skips the prompt before any work runs) |\n| `BATCH_UPGRADE_PACKAGES`    | Default for `--packages` (space-separated)           |\n| `BATCH_UPGRADE_VERSIONS`    | Default for `--versions` (space-separated)           |\n| `BATCH_UPGRADE_REPOS`       | Default for `--repos` (space-separated)              |\n| `BATCH_UPGRADE_BASE_BRANCH` | Default for `--base`                                 |\n| `BATCH_UPGRADE_YES=true`    | Default for `--yes`                                  |\n\nPrecedence: command-line flag \u003e environment variable \u003e interactive prompt \u003e error.\n\n## Exit codes\n\n| Code | Meaning                                             |\n| ---- | --------------------------------------------------- |\n| `0`  | Success                                             |\n| `1`  | One or more repositories failed                     |\n| `2`  | Usage error (invalid flags or arguments)            |\n| `3`  | GitHub CLI authentication failure                   |\n| `4`  | Repository or base branch not found                 |\n| `5`  | Dirty working tree (use `--reset-hard` to override) |\n\n## How it works\n\nFor each repository:\n\n1. **Pre-flight**: Verify the working tree is clean. If dirty without `--reset-hard`, abort with exit 5.\n2. **Branch**: Detect the base branch (`origin/HEAD` → fallback `main` → `master`), check it out, pull, and create `update-packages-YYYYMMDDHHmmss`.\n3. **Analyze**: For each package, locate it in `dependencies` / `devDependencies` / `peerDependencies` and skip when the current version is already `\u003e= target`.\n4. **Update**: Edit `package.json` (skipped in `--dry-run`).\n5. **Verify**: `npm install --force` then a clean `npm install` to catch lockfile/peer issues.\n6. **Commit \u0026 push**: Stage `package.json` + `package-lock.json`, commit, push to `origin`.\n7. **PR**: Open a pull request with `gh pr create --base \u003cdetected-branch\u003e`.\n8. **Cleanup**: If the diff was empty (everything skipped), delete the feature branch.\n\n## Output streams\n\n- **stdout**: machine-readable output only (the JSON summary when `--json` is set; completion scripts; `config get`/`list` output).\n- **stderr**: human-readable progress, warnings, and errors. Safe to discard in scripts (`2\u003e/dev/null`).\n\n## JSON output\n\nWhen `--json` is passed to `upgrade`, exactly one JSON object is written to stdout after the run completes. Progress, warnings, and errors continue to go to stderr.\n\n```json\n{\n  \"summary\": {\n    \"total\": 2,\n    \"succeeded\": 1,\n    \"failed\": 0,\n    \"skipped\": 1\n  },\n  \"repositories\": [\n    {\n      \"repo\": \"./web\",\n      \"status\": \"success\",\n      \"branch\": \"update-packages-20260511153400\",\n      \"baseBranch\": \"main\",\n      \"prUrl\": \"https://github.com/acme/web/pull/42\",\n      \"updates\": [\n        {\n          \"package\": \"react\",\n          \"fromVersion\": \"^17.0.2\",\n          \"toVersion\": \"^18.0.0\",\n          \"section\": \"dependencies\"\n        }\n      ],\n      \"error\": null,\n      \"errorCode\": null,\n      \"dryRun\": false\n    },\n    {\n      \"repo\": \"./admin\",\n      \"status\": \"skipped\",\n      \"branch\": \"update-packages-20260511153400\",\n      \"baseBranch\": \"main\",\n      \"prUrl\": null,\n      \"updates\": [],\n      \"error\": null,\n      \"errorCode\": null,\n      \"dryRun\": false\n    }\n  ],\n  \"dryRun\": false\n}\n```\n\n### Top-level fields\n\n| Field          | Type    | Description                                                 |\n| -------------- | ------- | ----------------------------------------------------------- |\n| `summary`      | object  | Aggregated counts across all repositories                   |\n| `repositories` | array   | One entry per repository in the same order as `--repos`     |\n| `dryRun`       | boolean | Whether the run was a `--dry-run` preview (no side effects) |\n\n### `summary` fields\n\n| Field       | Type   | Description                             |\n| ----------- | ------ | --------------------------------------- |\n| `total`     | number | Total number of repositories processed  |\n| `succeeded` | number | Repositories whose PR was created       |\n| `failed`    | number | Repositories that errored               |\n| `skipped`   | number | Repositories with no applicable updates |\n\n### `repositories[]` fields\n\n| Field        | Type           | Description                                                                                          |\n| ------------ | -------------- | ---------------------------------------------------------------------------------------------------- |\n| `repo`       | string         | Repository path as passed to `--repos`                                                               |\n| `status`     | string         | One of `\"success\"`, `\"failed\"`, `\"skipped\"`                                                          |\n| `branch`     | string \\| null | Feature branch name (`update-packages-YYYYMMDDHHmmss`); `null` if the run failed before branching    |\n| `baseBranch` | string \\| null | Detected (or `--base`-overridden) base branch; `null` if detection itself failed                     |\n| `prUrl`      | string \\| null | URL of the created pull request; `null` if no PR was created                                         |\n| `updates`    | array          | Per-package updates that were applied; empty when no packages matched or all were already up-to-date |\n| `error`      | string \\| null | Human-readable error message when `status === \"failed\"`                                              |\n| `errorCode`  | string \\| null | Stable token identifying the failure mode (see below); `null` on success/skip                        |\n| `dryRun`     | boolean        | Whether this repo was processed in `--dry-run` mode                                                  |\n\n### `updates[]` fields\n\n| Field         | Type   | Description                                                        |\n| ------------- | ------ | ------------------------------------------------------------------ |\n| `package`     | string | npm package name                                                   |\n| `fromVersion` | string | Version range that was in `package.json` before the update         |\n| `toVersion`   | string | Version range now in `package.json`                                |\n| `section`     | string | One of `\"dependencies\"`, `\"devDependencies\"`, `\"peerDependencies\"` |\n\n### `errorCode` values\n\n`GIT_CHECKOUT_FAILED`, `GIT_PULL_FAILED`, `GIT_BRANCH_FAILED`, `GIT_PUSH_FAILED`, `NPM_INSTALL_FORCE_FAILED`, `NPM_INSTALL_FAILED`, `GH_PR_CREATE_FAILED`, `CLI_ERROR_\u003cn\u003e` (where `\u003cn\u003e` is the exit code), or `UNKNOWN`.\n\nThe process exit code is `0` when `summary.failed === 0` and `1` otherwise. See [Exit codes](#exit-codes) above for usage/auth/not-found/dirty failures that exit before per-repo results are produced.\n\n## Programmatic API\n\nIn addition to the CLI, the package exports an ESM module you can drive from\nyour own Node.js scripts. The library returns the same result shape that\n`--json` prints to stdout.\n\n```js\nimport { updatePackages } from 'batch-upgrade-npm-packages';\n\nconst result = await updatePackages({\n  packages: ['react'],\n  versions: ['^18.0.0'],\n  repos: ['./web', './admin'],\n  resetHard: false,\n  baseBranch: null, // null = auto-detect from origin/HEAD\n});\n\nconsole.log(result.summary); // { total, succeeded, failed, skipped }\n```\n\nWith TypeScript, the result and options are fully typed:\n\n```ts\nimport { updatePackages } from 'batch-upgrade-npm-packages';\nimport type {\n  UpdatePackagesOptions,\n  UpdatePackagesResult,\n  RepositoryResult,\n} from 'batch-upgrade-npm-packages';\n```\n\nThe library still requires `gh` to be authenticated and the target repos to\nhave clean working trees (or `resetHard: true`). Per-repo failures populate\n`repositories[i].error` / `errorCode` rather than throwing, matching the CLI\ncontract. Top-level usage errors (no packages specified, gh not\nauthenticated, dirty tree without `resetHard`) throw a `CliError` whose\n`.code` matches the CLI exit-code table.\n\n## Safety\n\n- Refuses to touch repos with uncommitted changes unless you pass `--reset-hard`.\n- All changes land on a feature branch; the tool never commits to your base branch.\n- Validates `gh auth status` before doing any work; exits 3 with a hint if not authenticated.\n- Per-repo failures are isolated — the summary reports which repos succeeded, failed, and were skipped.\n- Arguments (package names, version strings, PR titles, PR bodies) are passed to subprocesses as argv arrays, never spliced into shell strings — shell-injection-safe.\n- `--dry-run` emits `[dry-run] Would run: …` lines for every mutating operation that would have executed.\n\n## Upgrading from 1.x?\n\nSee [MIGRATING.md](./MIGRATING.md) for the full 1.x → 2.0 migration guide, including the two changes most likely to break existing scripts (`-v` is no longer `--versions`, and confirmation now requires `--yes` / `CI=true` / non-TTY stdin).\n\n## Releasing\n\nReleases are manual, single-button. The maintainer:\n\n1. Opens a PR bumping `version` in `package.json` and adding a new `## [X.Y.Z]` entry in `CHANGELOG.md`. Merge to `main`.\n2. Opens **Actions → release → Run workflow** on `main`. Optionally checks `dry_run` to exercise the preflight without publishing.\n3. Approves the `npm-release` environment gate.\n\nThe workflow publishes to npm with provenance via OIDC, then creates the `vX.Y.Z` git tag and matching GitHub release. It refuses to run if the version already exists on npm or as a release — re-running after a successful release fails fast in preflight.\n\nCredentials: **npm Trusted Publishing (OIDC)**. No `NPM_TOKEN` is stored in the repo.\n\n## License\n\nMIT\n\n## Links\n\n- [npm Package](https://www.npmjs.com/package/batch-upgrade-npm-packages)\n- [Changelog](./CHANGELOG.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnnyshankman%2Fbatch-upgrade-npm-packages","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnnyshankman%2Fbatch-upgrade-npm-packages","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnnyshankman%2Fbatch-upgrade-npm-packages/lists"}