-
Notifications
You must be signed in to change notification settings - Fork 0
Add patch-only update mode #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ae477c4
6ca0406
add9f88
6a8c1a7
5ff3ef1
8c3a9aa
63469e7
623bd65
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,12 @@ | ||
| .PHONY: test testjs all | ||
| .PHONY: test testjs testjspatch all | ||
|
|
||
| all: test testjs | ||
| all: test testjs testjspatch | ||
|
|
||
| test: | ||
| go test ./... | ||
|
|
||
| testjs: | ||
| cd test_js && go run .. --dry-run --verbose | ||
|
|
||
| testjspatch: | ||
| cd test_js && go run .. --dry-run --verbose --patch-only |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -122,45 +122,37 @@ func getNPMPackageLatestVersion(packageName string) (string, error) { | |||||||||||||||||
| return result.Version, nil | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func outputLogger() *log.Logger { | ||||||||||||||||||
| if Ctx.Logger != nil { | ||||||||||||||||||
| return Ctx.Logger | ||||||||||||||||||
| func getOtherNPMPackageVersions(packageName string) ([]string, error) { | ||||||||||||||||||
| registryURL := "https://registry.npmjs.org/" + url.PathEscape(packageName) | ||||||||||||||||||
| client := Ctx.HTTPClient | ||||||||||||||||||
| if client == nil { | ||||||||||||||||||
| client = http.DefaultClient | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return log.Default() | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func formatDependencyDiff(before DependencyVersion, after DependencyVersion) string { | ||||||||||||||||||
| if before.HasSemver && after.HasSemver { | ||||||||||||||||||
| if before.Prefix == "" && after.Prefix == "" { | ||||||||||||||||||
| return before.Semver.Diff(after.Semver) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return fmt.Sprintf("%s -> %s (%s)", before.String(), after.String(), before.Semver.ChangeType(after.Semver)) | ||||||||||||||||||
| resp, err := client.Get(registryURL) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return []string{}, fmt.Errorf("request error: %w", err) | ||||||||||||||||||
| } | ||||||||||||||||||
| defer resp.Body.Close() | ||||||||||||||||||
|
|
||||||||||||||||||
| return fmt.Sprintf("%s -> %s", before.String(), after.String()) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (update DependencyUpdate) String() string { | ||||||||||||||||||
| return fmt.Sprintf("%s: %s", update.Name, formatDependencyDiff(update.Before, update.After)) | ||||||||||||||||||
| } | ||||||||||||||||||
| if resp.StatusCode != http.StatusOK { | ||||||||||||||||||
| return []string{}, fmt.Errorf("unexpected status: %d", resp.StatusCode) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func printDependencyUpdates(packagePath string, section string, updates []DependencyUpdate) { | ||||||||||||||||||
| if len(updates) == 0 { | ||||||||||||||||||
| return | ||||||||||||||||||
| var result struct { | ||||||||||||||||||
| Versions map[string]json.RawMessage `json:"versions"` | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| action := "Updated" | ||||||||||||||||||
| if Ctx.DryRun { | ||||||||||||||||||
| action = "Would update" | ||||||||||||||||||
| if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { | ||||||||||||||||||
| return []string{}, fmt.Errorf("JSON decode error: %w", err) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| logger := outputLogger() | ||||||||||||||||||
| logger.Print(action + " " + section + " in " + packagePath + ":") | ||||||||||||||||||
| for _, update := range updates { | ||||||||||||||||||
| logger.Print("- " + update.String()) | ||||||||||||||||||
| versions := make([]string, 0, len(result.Versions)) | ||||||||||||||||||
| for version := range result.Versions { | ||||||||||||||||||
| versions = append(versions, version) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return versions, nil | ||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func updateDependencies(deps []DependencyJSON) ([]DependencyUpdate, error) { | ||||||||||||||||||
|
|
@@ -188,8 +180,21 @@ func updateDependencies(deps []DependencyJSON) ([]DependencyUpdate, error) { | |||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if !shouldUpdate { | ||||||||||||||||||
| log.Debugf("Dependency %s already up to date (%s)", dep.Name, changeType) | ||||||||||||||||||
| continue | ||||||||||||||||||
| if !Ctx.PatchOnly || !dep.Version.HasSemver { | ||||||||||||||||||
| log.Debugf("Dependency %s won't update (%s)", dep.Name, changeType) | ||||||||||||||||||
| continue | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| patchVersion, ok, err := getLatestPatchNPMPackageVersion(dep.Name, dep.Version) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return nil, fmt.Errorf("failed to fetch other versions for %s: %w", dep.Name, err) | ||||||||||||||||||
| } | ||||||||||||||||||
| if !ok { | ||||||||||||||||||
| log.Debugf("Dependency %s has no available patch update", dep.Name) | ||||||||||||||||||
| continue | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| latestVersion = patchVersion | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| updatedVersion := mergeDependencyVersion(dep.Version, latestVersion) | ||||||||||||||||||
|
|
@@ -200,16 +205,48 @@ func updateDependencies(deps []DependencyJSON) ([]DependencyUpdate, error) { | |||||||||||||||||
| return updates, nil | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func getLatestPatchNPMPackageVersion(packageName string, currentVersion DependencyVersion) (DependencyVersion, bool, error) { | ||||||||||||||||||
| versions, err := getOtherNPMPackageVersions(packageName) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return DependencyVersion{}, false, err | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| var latestPatch DependencyVersion | ||||||||||||||||||
| foundPatch := false | ||||||||||||||||||
| for _, version := range versions { | ||||||||||||||||||
| versionToCheck := parseDependencyVersion(version) | ||||||||||||||||||
| changeTypeForVersionToCheck, shouldUpdateForVersionToCheck := classifyDependencyUpdate(currentVersion, versionToCheck) | ||||||||||||||||||
| if !shouldUpdateForVersionToCheck || changeTypeForVersionToCheck != SemverChangePatch { | ||||||||||||||||||
| continue | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if !foundPatch || latestPatch.Semver.LessThan(versionToCheck.Semver) { | ||||||||||||||||||
| latestPatch = versionToCheck | ||||||||||||||||||
| foundPatch = true | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return latestPatch, foundPatch, nil | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func classifyDependencyUpdate(currentVersion DependencyVersion, latestVersion DependencyVersion) (SemverChange, bool) { | ||||||||||||||||||
| if currentVersion.HasSemver && latestVersion.HasSemver { | ||||||||||||||||||
| changeType := currentVersion.Semver.ChangeType(latestVersion.Semver) | ||||||||||||||||||
| if Ctx.PatchOnly && changeType != SemverChangePatch { | ||||||||||||||||||
| return changeType, false | ||||||||||||||||||
| } | ||||||||||||||||||
| return changeType, changeType != SemverChangeNone && changeType != SemverChangeDowngrade | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if currentVersion.String() == latestVersion.String() { | ||||||||||||||||||
| return SemverChangeNone, false | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if Ctx.PatchOnly { | ||||||||||||||||||
| log.Warnf("Cannot determine change type for non-semver dependency version '%s' -> '%s', skipping update due to patch-only mode", currentVersion.String(), latestVersion.String()) | ||||||||||||||||||
| return SemverChangeNone, false | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+245
to
+248
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t classify non-semver patch-only skips as Line 218 currently reports “none” even when the real reason is “not semver-comparable under patch-only,” which can mislead logs and diagnostics. Return Suggested fix if Ctx.PatchOnly {
log.Warnf("Cannot determine change type for non-semver dependency version '%s' -> '%s', skipping update due to patch-only mode", currentVersion.String(), latestVersion.String())
- return SemverChangeNone, false
+ return SemverChangeInvalid, false
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| return SemverChangeInvalid, true | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -241,6 +278,47 @@ func depsToMap(deps []DependencyJSON) map[string]string { | |||||||||||||||||
| return depsMap | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func outputLogger() *log.Logger { | ||||||||||||||||||
| if Ctx.Logger != nil { | ||||||||||||||||||
| return Ctx.Logger | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return log.Default() | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func formatDependencyDiff(before DependencyVersion, after DependencyVersion) string { | ||||||||||||||||||
| if before.HasSemver && after.HasSemver { | ||||||||||||||||||
| if before.Prefix == "" && after.Prefix == "" { | ||||||||||||||||||
| return before.Semver.Diff(after.Semver) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return fmt.Sprintf("%s -> %s (%s)", before.String(), after.String(), before.Semver.ChangeType(after.Semver)) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| return fmt.Sprintf("%s -> %s", before.String(), after.String()) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (update DependencyUpdate) String() string { | ||||||||||||||||||
| return fmt.Sprintf("%s: %s", update.Name, formatDependencyDiff(update.Before, update.After)) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func printDependencyUpdates(packagePath string, section string, updates []DependencyUpdate) { | ||||||||||||||||||
| if len(updates) == 0 { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| action := "Updated" | ||||||||||||||||||
| if Ctx.DryRun { | ||||||||||||||||||
| action = "Would update" | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| logger := outputLogger() | ||||||||||||||||||
| logger.Print(action + " " + section + " in " + packagePath + ":") | ||||||||||||||||||
| for _, update := range updates { | ||||||||||||||||||
| logger.Print("- " + update.String()) | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func processNPMPackage(packagePath string) error { | ||||||||||||||||||
| packageJSONFile, err := os.ReadFile(packagePath) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
|
|
||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.