Skip to content

Commit 382e634

Browse files
lqiu96JoeWang1127
andauthored
feat: Force release for a specific library even if there are no releasable changes (#2181)
Fixes: #2032 Changes: - Split updateLibrary's GetCommits functionality into a separate function for easier testing (inject commits directly without having to setup a repo) Logic: If a library is specified for release, force a release for it. Use the library version override if provided as the new version. If the override is not specified, report an error back to the user to use the version flag. --------- Signed-off-by: Lawrence Qiu <[email protected]> Signed-off-by: Lawrence Qiu <[email protected]> Co-authored-by: Joe Wang <[email protected]>
1 parent 0b278f1 commit 382e634

File tree

3 files changed

+315
-178
lines changed

3 files changed

+315
-178
lines changed

internal/librarian/help.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ according to semver rules. It then delegates all language-specific file
8686
modifications, such as updating a CHANGELOG.md or bumping the version in a pom.xml,
8787
to the configured language-specific container.
8888
89+
If a specific library is configured for release via the '--library' flag, a single
90+
releasable change is needed to automatically calculate a version bump. If there are
91+
no releasable changes since the last release, the '--version' flag should be included
92+
to set a new version for the library. The new version must be "SemVer" greater than the
93+
current version.
94+
8995
By default, 'release init' leaves the changes in your local working directory
9096
for inspection. Use the '--push' flag to automatically commit the changes to
9197
a new branch and create a pull request on GitHub. The '--commit' flag may be

internal/librarian/release_init.go

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ func (r *initRunner) runInitCommand(ctx context.Context, outputDir string) error
132132

133133
src := r.repo.GetDir()
134134
librariesToRelease := r.state.Libraries
135-
// If a library has been specified, only process the release for it
136135
if r.library != "" {
137136
library := findLibraryByID(r.state, r.library)
138137
if library == nil {
@@ -143,9 +142,10 @@ func (r *initRunner) runInitCommand(ctx context.Context, outputDir string) error
143142
// Mark if there are any library that needs to be released
144143
foundReleasableLibrary := false
145144
for _, library := range librariesToRelease {
146-
if err := r.updateLibrary(library); err != nil {
145+
if err := r.processLibrary(library); err != nil {
147146
return err
148147
}
148+
149149
// Copy the library files over if a release is needed
150150
if library.ReleaseTriggered {
151151
foundReleasableLibrary = true
@@ -202,55 +202,66 @@ func (r *initRunner) runInitCommand(ctx context.Context, outputDir string) error
202202
return copyGlobalAllowlist(r.librarianConfig, r.repo.GetDir(), outputDir, false)
203203
}
204204

205-
// updateLibrary updates the given library in the following way:
206-
//
207-
// 1. Update the library's previous version.
208-
//
209-
// 2. Get the library's commit history in the given git repository.
210-
//
211-
// 3. Override the library version if libraryVersion is not empty.
212-
//
213-
// 4. Set the library's release trigger to true.
214-
func (r *initRunner) updateLibrary(library *config.LibraryState) error {
205+
// processLibrary wrapper to process the library for release. Helps retrieve latest commits
206+
// since the last release and passing the changes to updateLibrary.
207+
func (r *initRunner) processLibrary(library *config.LibraryState) error {
215208
commits, err := GetConventionalCommitsSinceLastRelease(r.repo, library)
216209
if err != nil {
217210
return fmt.Errorf("failed to fetch conventional commits for library, %s: %w", library.ID, err)
218211
}
212+
return r.updateLibrary(library, commits)
213+
}
219214

220-
if len(commits) == 0 {
221-
slog.Info("Skip releasing library as there are no commits", "library", library.ID)
222-
return nil
223-
}
224-
225-
nextVersion, err := r.determineNextVersion(commits, library.Version, library.ID)
226-
if err != nil {
227-
return err
215+
// updateLibrary updates the library's state with the new release information:
216+
//
217+
// 1. Determines the library version's next version.
218+
//
219+
// 2. Updates the library's previous version and the new current version.
220+
//
221+
// 3. Set the library's release trigger to true.
222+
func (r *initRunner) updateLibrary(library *config.LibraryState, commits []*conventionalcommits.ConventionalCommit) error {
223+
var nextVersion string
224+
// If library version was explicitly set, attempt to use it. Otherwise, try to determine the version from the commits.
225+
if r.libraryVersion != "" {
226+
slog.Info("Library version override inputted", "currentVersion", library.Version, "inputVersion", r.libraryVersion)
227+
nextVersion = semver.MaxVersion(library.Version, r.libraryVersion)
228+
slog.Debug("Determined the library's next version from version input", "library", library.ID, "nextVersion", nextVersion)
229+
// Currently, nextVersion is the max of current version or input version. If nextVersion is equal to the current version,
230+
// then the input version is either equal or less than current version and cannot be used for release
231+
if nextVersion == library.Version {
232+
return fmt.Errorf("inputted version is not SemVer greater than the current version. Set a version SemVer greater than current than: %s", library.Version)
233+
}
234+
} else {
235+
var err error
236+
nextVersion, err = r.determineNextVersion(commits, library.Version, library.ID)
237+
if err != nil {
238+
return err
239+
}
240+
slog.Debug("Determined the library's next version from commits", "library", library.ID, "nextVersion", nextVersion)
241+
// Unable to find a releasable unit from the changes
242+
if nextVersion == library.Version {
243+
// No library was inputted for release. Skipping this library for release
244+
if r.library == "" {
245+
slog.Info("Library does not have any releasable units and will not be released.", "library", library.ID, "version", library.Version)
246+
return nil
247+
}
248+
// Library was inputted for release, but does not contain a releasable unit
249+
return fmt.Errorf("library does not have a releasable unit and will not be released. Use the version flag to force a release for: %s", library.ID)
250+
}
251+
slog.Info("Updating library to the next version", "library", r.library, "currentVersion", library.Version, "nextVersion", nextVersion)
228252
}
229253

230254
// Update the previous version, we need this value when creating release note.
231255
library.PreviousVersion = library.Version
232256
library.Changes = commits
233-
// Only update the library state if there are releasable units
234-
if library.Version != nextVersion {
235-
library.Version = nextVersion
236-
library.ReleaseTriggered = true
237-
}
238-
257+
library.Version = nextVersion
258+
library.ReleaseTriggered = true
239259
return nil
240260
}
241261

262+
// determineNextVersion determines the next valid SemVer version from the commits or from
263+
// the next_version override value in the config.yaml file.
242264
func (r *initRunner) determineNextVersion(commits []*conventionalcommits.ConventionalCommit, currentVersion string, libraryID string) (string, error) {
243-
// If library version explicitly passed to CLI, use it
244-
if r.libraryVersion != "" {
245-
slog.Info("Library version override specified", "currentVersion", currentVersion, "version", r.libraryVersion)
246-
newVersion := semver.MaxVersion(currentVersion, r.libraryVersion)
247-
if newVersion == r.libraryVersion {
248-
return newVersion, nil
249-
} else {
250-
slog.Warn("Specified version is not higher than the current version, ignoring override.")
251-
}
252-
}
253-
254265
nextVersionFromCommits, err := NextVersion(commits, currentVersion)
255266
if err != nil {
256267
return "", err

0 commit comments

Comments
 (0)