Skip to content

Add Auto-PR Command - #1379

Merged
orto17 merged 16 commits into
jfrog:mainfrom
orto17:auto-fix-mode
Aug 27, 2026
Merged

Add Auto-PR Command#1379
orto17 merged 16 commits into
jfrog:mainfrom
orto17:auto-fix-mode

Conversation

@orto17

@orto17 orto17 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
  • All tests passed. If this feature is not already covered by the tests, I added new tests.
  • This pull request is on the dev branch.
  • I used gofmt for formatting the code before submitting the pull request.
  • Update documentation about new features / new supported technologies

Add Auto-PR command

Adds Auto-PR as a third Frogbot command alongside scan-pull-request and scan-repository.
Given a single vulnerable component, its affected version, and the version to upgrade to, Auto-PR locates the dependency descriptor that declares it, applies the version bump, and opens a fix pull request — without running a full repository scan first.
This makes it possible to drive a targeted fix from anywhere a CVE is already known: a Xray/JFrog Platform finding, a security ticket, or a manual workflow_dispatch.

How it works

  1. Resolves the fix branch name from the base branch, component, and fix version, and skips early if that branch already exists on the remote (a previously closed PR is not silently recreated).
  2. Verifies the worktree is clean, then generates an SBOM of the workspace with the Xray-Lib plugin.
  3. Matches the component in the SBOM to find every descriptor file that declares it, infers the package manager from the PURL type, and confirms the dependency is direct.
  4. Applies the upgrade using the shared GetCompatiblePackageUpdater factory from jfrog-cli-security, so Auto-PR and scan-repository bump versions through exactly the same updaters.
  5. Commits, pushes, and opens the PR through vcsclient, so it works on GitHub, GitLab, Bitbucket, and Azure Repos. The PR body reuses the standard Frogbot fix-PR content.
    Transitive dependencies are rejected with a clear message, since a manifest edit alone cannot fix them.

Supported package managers

Maven, npm, pnpm, Go, pip, and Docker. npm vs pnpm is disambiguated from workspace markers rather than the PURL, which cannot distinguish them.

- New `autofix` package: SBOM-based descriptor locator, dependency
  updater via jfrog-cli-security package updaters, git branch/commit/
  push via frogbot GitManager, VCS-agnostic PR creation via vcsclient
- Supports Maven, Npm, Pnpm, Go, Pip; technology inferred from PURL
- New `autofix/action.yml` subdirectory action (`uses: jfrog/frogbot/autofix@v3`)
  with dedicated inputs: component-name, affected-version, fix-version,
  branch-name, commit-hash
- New `.github/workflows/frogbot-auto-fix.yml` workflow_dispatch template
- Registered `auto-fix` (alias `af`) command in commands.go
- action/src/main.ts routes to execAutoFix() when command input is auto-fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Jul 13, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Jul 13, 2026
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Jul 27, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Jul 27, 2026
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Jul 27, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Jul 27, 2026
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Jul 27, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Jul 27, 2026
…rd the workspace

- Restore autopr/action.yml so jfrog/frogbot/autopr@v3 resolves again and routes
  to execAutoPr through the auto-pr command default, and add a CI smoke job that
  invokes the action by local path so a missing manifest fails the build.
- Resolve branch-name to the repository default branch in the workflow, matching
  the documented contract instead of landing in detached HEAD.
- Refuse to run auto-pr on a dirty worktree, and scope untracked-file cleanup to
  files that appeared after the updater ran, so a reused or local workspace never
  loses developer work.
- Fail rather than warn when cleanup fails, so a partially cleaned workspace is
  not swept into the fix commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Aug 26, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Aug 26, 2026
orto17 and others added 2 commits August 26, 2026 10:40
Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread autopr/autopr.go Outdated
Comment thread autopr/autopr.go Outdated
Comment thread .github/workflows/frogbot-auto-pr.yml Outdated
Comment thread .github/workflows/action-test.yml Outdated
Comment thread action/test/main.spec.ts Outdated
Comment thread autopr/autopr_test.go
Comment thread autopr/autopr.go Outdated
Comment thread autopr/autopr.go
Comment thread autopr/autopr.go
Comment thread scanrepository/scanrepository.go Outdated
Comment thread scanrepository/scanrepository.go Outdated
Comment thread utils/git.go Outdated
Comment thread utils/git.go Outdated
Comment thread autopr/locator.go Outdated
Comment thread autopr/locator.go
…coverage

Use the root action.yml for Auto-PR inputs, reuse security-cli BOM and PyPI helpers,
split Run into focused steps, skip expected Auto-PR cases from error telemetry, and
cover the success path plus not-found and transitive branches.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread autopr/autopr.go Outdated
Comment thread autopr/autopr.go
Comment thread autopr/autopr.go Outdated
Comment thread autopr/autopr.go
Comment thread autopr/locator.go Outdated
Comment thread autopr/autopr.go Outdated
Comment thread utils/git.go
Comment thread autopr/locator.go Outdated
…ace safety

- initialize the auto-pr git manager with the repository remote URL so remote
  branch checks and pushes work
- tolerate a missing config profile instead of panicking on env-var-only runs
- commit only tracked changes so files created during a run are never deleted
  or swept into the fix commit
- treat transitive dependencies as expected skips rather than telemetry errors
- pass only descriptor paths proven direct when a component appears both
  directly and transitively
- post pull request body overflow as follow-up comments
- apply PyPI and Maven name normalization only to their own ecosystems
Comment thread autopr/autopr.go
Comment on lines +166 to +185
func (a *AutoPrCmd) applyFixAndCreatePullRequest(run autoPrRun, repository utils.Repository, client vcsclient.VcsClient, gitManager autoPrGitManager) error {
if err := gitManager.CreateBranchAndCheckout(run.fixBranchName, false); err != nil {
return fmt.Errorf("failed to create fix branch '%s': %w", run.fixBranchName, err)
}
update := a.runUpdater
if update == nil {
update = runUpdater
}
if err := update(run.componentName, run.affectedVersion, run.fixVersion, run.tech, true, run.descriptorPaths); err != nil {
return err
}
commitMessage := gitManager.GenerateCommitMessage(run.componentName, run.fixVersion)
if err := gitManager.AddTrackedAndCommit(commitMessage, run.componentName); err != nil {
var errNoChanges *utils.ErrNothingToCommit
if errors.As(err, &errNoChanges) {
log.Info(err.Error())
return &ErrAutoPrSkipped{Reason: err.Error()}
}
return fmt.Errorf("failed to commit changes: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: After CreateBranchAndCheckout there is no restore on updater/commit/push failure.

The caller is left on a local fix branch with tracked modifications (and any untracked updater/SBOM leftovers). A later retry then fails the clean-worktree check.

Please restore the original branch and discard Auto-PR mutations on those error paths (or run in a disposable worktree/clone). Success can keep the new branch; failure should not.

Comment thread utils/git.go
Comment on lines +318 to +342
func (gm *GitManager) AddTrackedAndCommit(commitMessage, impactedDependencyName string) error {
worktree, err := gm.localGitRepository.Worktree()
if err != nil {
return err
}
status, err := worktree.Status()
if err != nil {
return err
}
hasTrackedChanges := false
for fileName, fileStatus := range status {
if fileStatus.Worktree == git.Untracked || fileStatus.Staging == git.Added {
continue
}
if _, err = worktree.Add(fileName); err != nil {
return err
}
hasTrackedChanges = true
}
if !hasTrackedChanges {
return &ErrNothingToCommit{PackageName: impactedDependencyName}
}
return gm.commit(commitMessage)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: AddTrackedAndCommit correctly avoids committing untracked files, but those files are never cleaned up.

SBOM/plugin/updater leftovers remain after both success and failure, so the worktree is dirty and the next auto-pr run is rejected.

Either isolate the run in a disposable worktree, or restore the exact pre-run state (tracked + untracked) without deleting files that existed before the command started.

Comment thread autopr/autopr.go
}

func (a *AutoPrCmd) applyFixAndCreatePullRequest(run autoPrRun, repository utils.Repository, client vcsclient.VcsClient, gitManager autoPrGitManager) error {
if err := gitManager.CreateBranchAndCheckout(run.fixBranchName, false); err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: The fix branch is created from the current HEAD, not from run.baseBranch / commit-hash.

JF_COMMIT_HASH is only appended to the PR body; nothing checks out that commit. JF_GIT_BASE_BRANCH is used as the PR target, not as the git parent. A clean checkout on the wrong branch (or a SHA that does not match the finding) produces a PR with unrelated commits.

Please branch from the requested base (and check out commit-hash when it is set), or fail if HEAD is not that revision.

Comment thread autopr/locator.go
Comment on lines +77 to +109
if match.purlType == "" {
match.purlType = compType
}
relation := bomIndex.GetComponentRelation(component.BOMRef)
isDirect := relation == cdxutils.RootRelation || relation == cdxutils.DirectRelation
if isDirect {
match.isDirectFromRoot = true
}

for _, location := range results.CdxEvidencesToLocations(component) {
if location.File == "" {
continue
}
if isDirect {
if directSeen[location.File] {
continue
}
directSeen[location.File] = true
directPaths = append(directPaths, location.File)
} else {
if transitiveSeen[location.File] {
continue
}
transitiveSeen[location.File] = true
transitivePaths = append(transitivePaths, location.File)
}
log.Debug(fmt.Sprintf("Found descriptor: %s", location.File))
}
}
match.descriptorPaths = directPaths
if !match.isDirectFromRoot {
match.descriptorPaths = transitivePaths
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important: Same bare name+version across ecosystems can still be merged.

purlType is taken from the first match, while direct descriptor paths are collected from every matching component. In a polyglot repo that can pick the wrong updater.

If more than one PURL type matches, fail with an explicit “ambiguous component” error (or require a typed Xray component ID). A fixture with npm + pypi sharing a name/version would lock this in.

Comment thread autopr/autopr_test.go
}
}

func TestInitializeGitManager_WithNilConfigProfileAndConfiguredRemote(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: This only asserts a non-nil GitManager.

It does not check remoteGitUrl / BranchExistsInRemote, so the previous empty-remote regression could come back unnoticed. Please assert the configured clone URL (or a list/push against a real local remote).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature Automatically generated release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants