greenbump 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,54 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ## [0.2.1]
7
+
8
+ ### Added
9
+ - `--scan` flag: list outdated dependencies without upgrading (read-only mode)
10
+ - Risk boundaries documentation in README (when to use greenbump vs manual review)
11
+ - Competitor comparison table (vs Dependabot, Renovate, Migratowl)
12
+ - CI smoke tests for real toolchains (npm, pip, cargo)
13
+ - Options table updated with `--scan`, `--all`, `--group`, `--max-tokens`
14
+
15
+ ### Changed
16
+ - GitHub Action branding icon from `arrow-up-circle` to `refresh-cw`
17
+ - npm package excludes test files via `.npmignore` (reduced from 116 to 114 files)
18
+
19
+ ## [0.2.0]
20
+
21
+ ### Added
22
+ - Batch upgrades: `--all` (every outdated dep) and multi-dep args, with `--group <name>`
23
+ to combine several deps into one branch/PR, and `--fail-fast` to abort a batch on the
24
+ first hard failure.
25
+ - Monorepo/workspace support: detects npm/yarn `workspaces` and pnpm's
26
+ `pnpm-workspace.yaml`, resolving ambiguous dependency names via `--workspace`.
27
+ - `needsReview` field on run summaries, surfaced as CLI exit code 3 (distinct from exit
28
+ code 2 for an unfixed upgrade) — flags runs where a human should look before merging
29
+ (a test file was touched to get green, the run was unverifiable, or a `--max-tokens`
30
+ budget was hit).
31
+ - `--max-tokens`: hard cap on total tokens spent by the fix loop. Stops the loop and
32
+ flags `needsReview` instead of silently overspending.
33
+ - `--report-file <path>`: persist a JSON report (schema-versioned) of the run(s).
34
+ - GitHub Action: PR draft/label updates driven by `needsReview`, new `review-label`
35
+ input (default `needs-review`), and `report-file` input.
36
+ - Parser unit tests for the 7 ecosystems that had none: npm, pnpm, pip, uv, pipenv,
37
+ swiftpm, elm.
38
+
39
+ ### Changed
40
+ - Clarified in the docs and in `EcosystemAdapter.verified`'s own type comment that
41
+ `verified: true` is a self-reported, one-time manual smoke test by the maintainer —
42
+ not an automated, CI-backed guarantee.
43
+
44
+ ### Fixed
45
+ - A CI-only flaky test in `checks.test.ts` caused by cross-stream (stdout/stderr)
46
+ write-ordering not being guaranteed when appended into one combined buffer.
47
+
48
+ ## [0.1.0]
49
+
50
+ Initial release: single-dependency upgrade via `greenbump <dep>` or the GitHub Action,
51
+ auto-detecting the ecosystem (npm, Yarn, pnpm, pip, Poetry, uv, Pipenv, Cargo, Go
52
+ modules, Bundler, Composer, Gradle, Maven, NuGet, Mix, Pub, Swift Package Manager,
53
+ CocoaPods, Conan, Elm), running the project's build/test, and handing any breakage to
54
+ an AI fix agent (Anthropic/OpenAI-compatible providers) until checks pass again.
@@ -0,0 +1,52 @@
1
+ # Contributing
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ npm install
7
+ npm run build
8
+ npm test
9
+ ```
10
+
11
+ `npm test` builds the project then runs every `*.test.js` file under `dist/`
12
+ with Node's built-in test runner. Some tests (`git.test.ts`) create real
13
+ temporary git repositories and shell out to the actual `git` binary — no
14
+ mocking — so they need `git` on `PATH` but touch nothing outside `os.tmpdir()`.
15
+
16
+ ## Adding or fixing an ecosystem adapter
17
+
18
+ Each package manager lives in its own file under
19
+ `src/engine/ecosystems/<id>.ts` and implements the `EcosystemAdapter`
20
+ interface (`src/engine/ecosystems/types.ts`): `detect`, `outdated`, `install`,
21
+ `defaultCheckCommands`.
22
+
23
+ If you're fixing a parser bug, please:
24
+ 1. Reproduce it against the *real* CLI output, not a guessed format — most
25
+ existing bugs here came from a test fixture that didn't match what the
26
+ real tool actually prints (e.g. `mix hex.outdated`'s "Only" column, or
27
+ `pod outdated`'s `(latest version X)` suffix).
28
+ 2. Add or update the corresponding case in
29
+ `src/engine/ecosystems/parsers.test.ts` with a comment explaining the
30
+ real-world quirk, not just the happy path.
31
+ 3. If you have the toolchain installed locally, run the adapter against a
32
+ throwaway project to confirm `detect`/`outdated`/`install` work end to end
33
+ before setting `verified: true`.
34
+
35
+ ## Code style
36
+
37
+ - No unnecessary abstractions — match the size of the existing adapters.
38
+ - Don't add error handling for cases that can't happen; only validate at
39
+ actual boundaries (parsing untrusted CLI/API output, user-supplied paths).
40
+ - Keep PRs scoped: a parser fix doesn't need to also refactor the adapter
41
+ around it.
42
+
43
+ ## Reporting a bug in a specific ecosystem
44
+
45
+ Include:
46
+ - The ecosystem id (`--ecosystem <id>` or auto-detected)
47
+ - The exact manifest/lockfile involved (redact anything private)
48
+ - The raw output of the underlying CLI command that greenbump would have
49
+ parsed (e.g. `npm outdated --json`, `pod outdated`)
50
+
51
+ For security issues, see [SECURITY.md](SECURITY.md) instead of opening a
52
+ public issue.
package/README.md CHANGED
@@ -34,6 +34,40 @@ npx greenbump react
34
34
  - **Safe by default.** Works on a fresh git branch, never auto-merges, never edits your lockfile to cheat, never deletes tests to go green.
35
35
  - **Bounded cost.** The fix loop is capped (`--max-rounds`) so a hard upgrade can't run away with your tokens.
36
36
 
37
+ ## When to use greenbump (and when not to)
38
+
39
+ **✓ Great for:**
40
+ - Minor/patch upgrades with small breaking changes (renamed exports, updated function signatures)
41
+ - Dependency chains where the upgrade path is documented in changelogs
42
+ - Projects with fast, reliable build + test suites that catch regressions
43
+
44
+ **⚠️ Use caution or manual review:**
45
+ - Major version jumps with architectural changes (e.g., Angular 2 → 3, React 15 → 16)
46
+ - Upgrades that require coordinated changes across multiple dependencies
47
+ - Security patches where you need to audit the fix, not just make tests pass
48
+ - Projects without tests — greenbump can only verify what your suite checks
49
+
50
+ **✗ Not recommended:**
51
+ - Upgrades that break core framework assumptions (e.g., async/await refactors, build system migrations)
52
+ - Dependencies with runtime-only failures your tests don't cover
53
+ - Scenarios where "tests pass" doesn't mean "the app works" (missing integration tests, flaky suites)
54
+
55
+ greenbump works best as a first pass on straightforward upgrades. Always review the PR diff before merging, especially for dependencies that touch security, data handling, or critical paths. Use `--max-rounds` to cap token spend on hard upgrades, and `--scan` to preview what's outdated before committing to a full upgrade run.
56
+
57
+ ## How is greenbump different?
58
+
59
+ | Feature | greenbump | Dependabot | Renovate | Migratowl |
60
+ |---------|-----------|------------|----------|-----------|
61
+ | **Auto-fix breaking changes** | ✓ (LLM edits source) | ✗ (version bump only) | ✗ (version bump only) | ✓ (LLM + sandbox) |
62
+ | **Multi-ecosystem** | ✓ (20+ ecosystems) | ✓ | ✓ | ✗ (Python only) |
63
+ | **Model flexibility** | ✓ (Claude, DeepSeek, OpenAI, custom) | N/A | N/A | ✗ (Claude only) |
64
+ | **Verification** | Your build + tests | GitHub's CI (after PR) | CI after PR | Sandbox + your tests |
65
+ | **Cost model** | Your API key (pay per fix) | Free (GitHub-hosted) | Free (self-host or SaaS) | Your API key |
66
+ | **Batch upgrades** | ✓ (`--all`) | ✓ | ✓ | ✗ |
67
+ | **Best for** | Fast iteration on minor/patch bumps with simple breaking changes | No-touch version bumps, large fleets | Advanced scheduling, auto-merge rules | High-risk major upgrades needing sandbox isolation |
68
+
69
+ **greenbump is the only tool that combines multi-ecosystem support + actual code fixing + model flexibility.** Dependabot and Renovate excel at *detecting* outdated dependencies and *opening* PRs, but stop there — you still write the fix. Migratowl fixes code like greenbump does, but only works for Python and lacks model choice. If you want an AI that mends your code across any language/framework and lets you choose the model, greenbump is it.
70
+
37
71
  ## Install / Use
38
72
 
39
73
  ```bash
@@ -62,6 +96,10 @@ npx greenbump
62
96
  | `--provider <name>` | Model provider preset (`anthropic`, `openai`, `deepseek`, `groq`, …). |
63
97
  | `--model <model>` | Model id for the fix agent (default: per provider). |
64
98
  | `--max-rounds <n>` | Cap fix-loop rounds / token spend (default: `15`). |
99
+ | `--max-tokens <n>` | Hard cap on total tokens spent; stops and flags for review on overrun. |
100
+ | `--scan` | List outdated dependencies without upgrading (read-only mode). |
101
+ | `--all` | Upgrade every outdated dependency found. |
102
+ | `--group <name>` | Combine multiple deps into one branch/PR. |
65
103
  | `--no-git` | Operate in place instead of on a new branch. |
66
104
  | `--pr-body` | Print a ready-to-paste PR body. |
67
105
 
package/SECURITY.md ADDED
@@ -0,0 +1,38 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Please do **not** open a public GitHub issue for security vulnerabilities.
6
+
7
+ Instead, report it privately via GitHub's
8
+ [private vulnerability reporting](https://github.com/shidesheng0218/greenbump/security/advisories/new)
9
+ (https://github.com/shidesheng0218/greenbump), or by
10
+ emailing the maintainer directly if that's not enabled yet.
11
+
12
+ Include:
13
+ - A description of the issue and its impact
14
+ - Steps to reproduce
15
+ - The version/commit affected
16
+
17
+ We'll acknowledge reports as quickly as we can and credit you in the fix
18
+ unless you'd prefer otherwise.
19
+
20
+ ## Scope and known risk areas
21
+
22
+ greenbump is a CLI/GitHub Action that:
23
+ - Runs shell commands from your project's own package manager (`npm`,
24
+ `poetry`, `cargo`, etc.) against your working directory.
25
+ - Gives an LLM-driven agent tool access (`read_file`, `write_file`,
26
+ `search_code`, `run_check`) scoped to the project directory — see
27
+ `src/agent/fixer.ts`'s `safePath()` for the path-escape guard.
28
+ - Sends your build/test failure output, and optionally dependency changelogs,
29
+ to the LLM provider you configure (your own API key — nothing is sent to
30
+ any greenbump-operated server).
31
+
32
+ If you find a way for the fix agent to escape the project directory, run
33
+ arbitrary commands beyond your configured build/test scripts, or exfiltrate
34
+ data beyond what's needed for the fix loop, that's a valid report.
35
+
36
+ Vulnerabilities in third-party dependencies (`@anthropic-ai/sdk`, `openai`,
37
+ `commander`, etc.) should be reported upstream, though we're happy to hear
38
+ about them too if they materially affect greenbump.
package/action.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  name: "greenbump"
2
2
  description: "Upgrade a dependency and let an AI agent fix the code it breaks, then open a green PR."
3
3
  branding:
4
- icon: "arrow-up-circle"
4
+ icon: "refresh-cw"
5
5
  color: "green"
6
6
  inputs:
7
7
  anthropic-api-key:
package/dist/cli.js CHANGED
@@ -10,6 +10,8 @@ import { formatDuration, printSummaryBox, printBatchSummary } from "./format.js"
10
10
  import { listEcosystems } from "./engine/ecosystems/index.js";
11
11
  import { exitCodeFor, overallExitCode } from "./exitcode.js";
12
12
  import { buildReport, writeReport } from "./report.js";
13
+ import { detectOutdatedAll, WORKSPACE_ROOT } from "./engine/workspace.js";
14
+ import { detectPackageManager } from "./engine/pm.js";
13
15
  const program = new Command();
14
16
  program
15
17
  .name("greenbump")
@@ -35,6 +37,7 @@ program
35
37
  .option("--group <name>", "combine multiple deps named on the command line into one branch/PR")
36
38
  .option("--fail-fast", "abort a batch on the first hard failure instead of continuing")
37
39
  .option("--workspace <path>", "disambiguate a dep that's outdated at different versions in multiple workspace packages")
40
+ .option("--scan", "list outdated dependencies without upgrading (read-only mode)")
38
41
  .action(async (deps, opts) => {
39
42
  if (opts.listProviders) {
40
43
  console.log("Built-in provider presets:\n" + listProviders());
@@ -46,6 +49,25 @@ program
46
49
  console.log("Supported dependency ecosystems:\n" + listEcosystems());
47
50
  process.exit(0);
48
51
  }
52
+ if (opts.scan) {
53
+ const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();
54
+ const pm = opts.ecosystem || await detectPackageManager(cwd);
55
+ if (!pm) {
56
+ console.error(pc.red("✗ Could not detect package manager. Use --ecosystem to specify one."));
57
+ process.exit(1);
58
+ }
59
+ const outdated = await detectOutdatedAll(pm, cwd);
60
+ if (outdated.length === 0) {
61
+ console.log(pc.green("✓ All dependencies are up to date."));
62
+ process.exit(0);
63
+ }
64
+ console.log(pc.bold(`Found ${outdated.length} outdated dependencies:\n`));
65
+ for (const o of outdated) {
66
+ const loc = o.workspacePath === WORKSPACE_ROOT ? "(root)" : o.workspacePath;
67
+ console.log(` ${pc.cyan(o.name)} ${pc.dim(o.current)} → ${pc.green(o.latest)} ${pc.dim(`[${loc}]`)}`);
68
+ }
69
+ process.exit(0);
70
+ }
49
71
  const choice = {
50
72
  provider: opts.provider,
51
73
  model: opts.model,
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CACV,kGAAkG,CACnG;KACA,QAAQ,CAAC,WAAW,EAAE,yGAAyG,CAAC;KAChI,MAAM,CAAC,cAAc,EAAE,wDAAwD,CAAC;KAChF,MAAM,CAAC,gBAAgB,EAAE,uEAAuE,CAAC;KACjG,MAAM,CAAC,kBAAkB,EAAE,+EAA+E,CAAC;KAC3G,MAAM,CAAC,mBAAmB,EAAE,oCAAoC,CAAC;KACjE,MAAM,CAAC,mBAAmB,EAAE,iDAAiD,CAAC;KAC9E,MAAM,CAAC,kBAAkB,EAAE,+CAA+C,CAAC;KAC3E,MAAM,CAAC,mBAAmB,EAAE,8DAA8D,CAAC;KAC3F,MAAM,CAAC,iBAAiB,EAAE,oDAAoD,CAAC;KAC/E,MAAM,CAAC,kBAAkB,EAAE,0DAA0D,CAAC;KACtF,MAAM,CAAC,iBAAiB,EAAE,qDAAqD,CAAC;KAChF,MAAM,CAAC,kBAAkB,EAAE,yCAAyC,CAAC;KACrE,MAAM,CAAC,kBAAkB,EAAE,wCAAwC,EAAE,IAAI,CAAC;KAC1E,MAAM,CAAC,kBAAkB,EAAE,uFAAuF,CAAC;KACnH,MAAM,CAAC,UAAU,EAAE,+CAA+C,CAAC;KACnE,MAAM,CAAC,WAAW,EAAE,4CAA4C,CAAC;KACjE,MAAM,CAAC,sBAAsB,EAAE,gDAAgD,CAAC;KAChF,MAAM,CAAC,OAAO,EAAE,yCAAyC,CAAC;KAC1D,MAAM,CAAC,gBAAgB,EAAE,oEAAoE,CAAC;KAC9F,MAAM,CAAC,aAAa,EAAE,+DAA+D,CAAC;KACtF,MAAM,CAAC,oBAAoB,EAAE,yFAAyF,CAAC;KACvH,MAAM,CAAC,QAAQ,EAAE,+DAA+D,CAAC;KACjF,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAC3B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,8BAA8B,GAAG,aAAa,EAAE,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,qEAAqE,CAAC,CAAC;QACnF,OAAO,CAAC,GAAG,CAAC,oFAAoF,CAAC,CAAC;QAClG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,oCAAoC,GAAG,cAAc,EAAE,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACzD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC7D,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,qEAAqE,CAAC,CAAC,CAAC;YAC7F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAClD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAC;YAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,MAAM,2BAA2B,CAAC,CAAC,CAAC;QAC1E,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,CAAC,CAAC,aAAa,KAAK,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;YAC5E,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QACzG,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG;QACb,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC;IACF,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CACX,EAAE,CAAC,GAAG,CAAC,uBAAuB,CAAC;YAC7B,mEAAmE,CACtE,CAAC;QACF,OAAO,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;QACxE,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QACtE,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QACtE,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QAC7C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC,CAAC;QAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE;QACxB,MAAM,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QACvE,OAAO,CAAC,KAAK,CAAC,KAAK,OAAO,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC;IACF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACzD,MAAM,QAAQ,GAAG;QACf,GAAG;QACH,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;QACvC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;QACpE,KAAK,EAAE,IAAI,CAAC,GAAG,KAAK,KAAK;QACzB,KAAK,EAAE,GAAG;KACX,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAE5C,IAAI,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;QAEjD,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,QAAQ,CAAC;gBACjC,GAAG,QAAQ;gBACX,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;gBACjC,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB,CAAC,CAAC;YAEH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAClB,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YAE1C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACpB,MAAM,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;YAC3D,CAAC;YAED,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC;YACxB,GAAG,QAAQ;YACX,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YACZ,EAAE,EAAE,IAAI,CAAC,EAAE;SACZ,CAAC,CAAC;QAEH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClB,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAExC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClB,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,uBAAwB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,UAAU,EAAE,CAAC"}
@@ -0,0 +1,37 @@
1
+ # Copy this to .github/workflows/greenbump.yml in your repo.
2
+ # Example GitHub Actions workflow for greenbump
3
+ # See https://github.com/shidesheng0218/greenbump for full documentation
4
+ name: greenbump
5
+
6
+ on:
7
+ schedule:
8
+ - cron: "0 6 * * 1" # every Monday 06:00 UTC
9
+ workflow_dispatch: # or run it by hand, optionally naming a dependency
10
+ inputs:
11
+ dependency:
12
+ description: "Dependency to upgrade (blank = most outdated)"
13
+ required: false
14
+
15
+ permissions:
16
+ contents: write
17
+ pull-requests: write
18
+
19
+ jobs:
20
+ bump:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - uses: actions/setup-node@v4
26
+ with:
27
+ node-version: 20
28
+
29
+ - run: npm ci
30
+
31
+ - uses: shidesheng0218/greenbump@v0
32
+ with:
33
+ anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
34
+ dependency: ${{ inputs.dependency }}
35
+ # Optional: use a PAT instead of the default token if you want
36
+ # the generated PR to trigger your CI workflows.
37
+ # github-token: ${{ secrets.GREENBUMP_PAT }}
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "legacy-repo",
3
+ "version": "1.0.0",
4
+ "description": "Sample repo with a deliberately outdated dependency, for greenbump e2e testing.",
5
+ "private": true,
6
+ "scripts": {
7
+ "test": "node test.js"
8
+ },
9
+ "dependencies": {
10
+ "uuid": "3.4.0"
11
+ }
12
+ }
@@ -0,0 +1,17 @@
1
+ const assert = require("node:assert");
2
+ const { newId } = require("./src/id.js");
3
+
4
+ // A real test that asserts behaviour — greenbump must NOT weaken this to pass.
5
+ const id = newId();
6
+ assert.strictEqual(typeof id, "string", "id must be a string");
7
+ assert.match(
8
+ id,
9
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,
10
+ "id must be a valid UUID",
11
+ );
12
+
13
+ const a = newId();
14
+ const b = newId();
15
+ assert.notStrictEqual(a, b, "ids must be unique");
16
+
17
+ console.log("ok - newId() returns unique valid UUIDs");
@@ -0,0 +1,8 @@
1
+ // Breaks on uuid v7+: the `uuid/v4` subpath import was removed.
2
+ const uuidv4 = require("uuid/v4");
3
+
4
+ function createTodo(title) {
5
+ return { id: uuidv4(), title, done: false };
6
+ }
7
+
8
+ module.exports = { createTodo };
@@ -0,0 +1,11 @@
1
+ // Breaks on uuid v7+: the `uuid/v4` subpath import was removed.
2
+ // Note the different local name (`generateId`) from models/todo.js —
3
+ // a fix agent that patches one call site and re-runs tests will only
4
+ // see this one break AFTER fixing todo.js, one at a time.
5
+ const generateId = require("uuid/v4");
6
+
7
+ function createUser(name) {
8
+ return { id: generateId(), name };
9
+ }
10
+
11
+ module.exports = { createUser };
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "multi-file-repo",
3
+ "version": "1.0.0",
4
+ "description": "Sample repo where one dependency upgrade breaks 3 separate files, for greenbump e2e testing of search_code.",
5
+ "private": true,
6
+ "scripts": {
7
+ "test": "node test.js"
8
+ },
9
+ "dependencies": {
10
+ "uuid": "3.4.0"
11
+ }
12
+ }
@@ -0,0 +1,9 @@
1
+ // Breaks on uuid v7+: the `uuid/v4` subpath import was removed.
2
+ // Yet another local name (`newUUID`) — third independent call site.
3
+ const newUUID = require("uuid/v4");
4
+
5
+ function startSession(userId) {
6
+ return { token: newUUID(), userId, startedAt: Date.now() };
7
+ }
8
+
9
+ module.exports = { startSession };
@@ -0,0 +1,19 @@
1
+ const assert = require("node:assert");
2
+ const { createTodo } = require("./models/todo.js");
3
+ const { createUser } = require("./models/user.js");
4
+ const { startSession } = require("./services/session.js");
5
+
6
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
7
+
8
+ const todo = createTodo("write tests");
9
+ assert.match(todo.id, UUID_RE, "todo id must be a valid UUID");
10
+ assert.strictEqual(todo.title, "write tests");
11
+
12
+ const user = createUser("ada");
13
+ assert.match(user.id, UUID_RE, "user id must be a valid UUID");
14
+
15
+ const session = startSession(user.id);
16
+ assert.match(session.token, UUID_RE, "session token must be a valid UUID");
17
+ assert.strictEqual(session.userId, user.id);
18
+
19
+ console.log("ok - todo, user, and session all generate valid UUIDs");
package/package.json CHANGED
@@ -1,16 +1,11 @@
1
1
  {
2
2
  "name": "greenbump",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Upgrade a dependency and let an AI agent fix the code it breaks — until CI is green. The Renovate that actually mends your code.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "greenbump": "dist/cli.js"
8
8
  },
9
- "files": [
10
- "dist",
11
- "action-dist",
12
- "action.yml"
13
- ],
14
9
  "engines": {
15
10
  "node": ">=20"
16
11
  },