Skip to content

Commit

Permalink
feat(ruby): add Gemfile.Lock updater (#1790)
Browse files Browse the repository at this point in the history
Adds support for updating the ruby gem version in a committed
Gemfile.lock file. This helps ensure that bundle install works correctly
after a version.rb version change. There is also some additional logic
introduced to simulate how ruby Gem::Version handles `-` with prerelease
semvers (e.g. `1.0.0-alpha` is parsed as `1.0.0.pre.alpha).

A future update could make use of the new common stringifyRubyVersion to
translate the version into the more commonly used `.` prerelease seperatorwhich
is treated "as is" and avoids the `.pre.` replacement of `-`.
  • Loading branch information
andrewthauer committed Jan 5, 2023
1 parent 9c94e0c commit 9baf736
Show file tree
Hide file tree
Showing 12 changed files with 620 additions and 4 deletions.
127 changes: 127 additions & 0 deletions __snapshots__/gemfile-lock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
exports['Gemfile.lock updateContent updates prerelease in Gemfile.lock 1'] = `
PATH
remote: .
specs:
foo (0.2.0.pre.alpha)
GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
foobar (1.0.1)
diff-lcs (1.5.0)
json (2.6.3)
parallel (1.22.1)
parser (3.1.3.0)
ast (~> 2.4.1)
rainbow (3.1.1)
rake (13.0.6)
regexp_parser (2.6.1)
rexml (3.2.5)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.0)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-support (3.12.0)
rubocop (1.39.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.23.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.24.0)
parser (>= 3.1.1.0)
ruby-progressbar (1.11.0)
unicode-display_width (2.3.0)
PLATFORMS
ruby
DEPENDENCIES
bundler
foo!
foobar
rake
rspec
rubocop
BUNDLED WITH
2.3.26
`

exports['Gemfile.lock updateContent updates version in Gemfile.lock 1'] = `
PATH
remote: .
specs:
foo (0.2.0)
GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
foobar (1.0.1)
diff-lcs (1.5.0)
json (2.6.3)
parallel (1.22.1)
parser (3.1.3.0)
ast (~> 2.4.1)
rainbow (3.1.1)
rake (13.0.6)
regexp_parser (2.6.1)
rexml (3.2.5)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.0)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-support (3.12.0)
rubocop (1.39.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.23.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.24.0)
parser (>= 3.1.1.0)
ruby-progressbar (1.11.0)
unicode-display_width (2.3.0)
PLATFORMS
ruby
DEPENDENCIES
bundler
foo!
foobar
rake
rspec
rubocop
BUNDLED WITH
2.3.26
`
25 changes: 25 additions & 0 deletions __snapshots__/version-rb.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,31 @@ end
`

exports['version.rb updateContent updates prerelease versions in version.rb 1'] = `
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Google
module Cloud
module Bigtable
VERSION = "10.0.0-alpha1".freeze
end
end
end
`

exports['version.rb updateContent updates version in version.rb 1'] = `
# Copyright 2019 Google LLC
#
Expand Down
11 changes: 11 additions & 0 deletions src/strategies/ruby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {Changelog} from '../updaters/changelog';

// Ruby
import {VersionRB} from '../updaters/ruby/version-rb';
import {GemfileLock} from '../updaters/ruby/gemfile-lock';
import {BaseStrategy, BuildUpdatesOptions, BaseStrategyOptions} from './base';
import {ConventionalCommit} from '../commit';
import {Update} from '../update';
Expand Down Expand Up @@ -56,6 +57,16 @@ export class Ruby extends BaseStrategy {
version,
}),
});

updates.push({
path: this.addPath('Gemfile.lock'),
createIfMissing: false,
updater: new GemfileLock({
version,
gemName: this.component || '',
}),
});

return updates;
}

Expand Down
52 changes: 52 additions & 0 deletions src/updaters/ruby/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {Version} from '../../version';

// Ruby gem semver strings using `.` seperator for prereleases rather then `-`
// See https://guides.rubygems.org/patterns/

export const RUBY_VERSION_REGEX = /((\d+).(\d)+.(\d+)(.\w+.*)?)/g;

/**
* Stringify a version to a ruby compatible version string
*
* @param version The version to stringify
* @param useDotPrePreleaseSeperator Use a `.` seperator for prereleases rather then `-`
* @returns a ruby compatible version string
*/
export function stringifyRubyVersion(
version: Version,
useDotPrePreleaseSeperator = false
) {
if (!useDotPrePreleaseSeperator) {
return version.toString();
}

return `${version.major}.${version.minor}.${version.patch}${
version.preRelease ? `.${version.preRelease}` : ''
}`;
}

/**
* This function mimics Gem::Version parsing of version semver strings
*
* @param versionString The version string to resolve
* @returns A Gem::Version compatible version string
*/
export function resolveRubyGemfileLockVersion(versionString: string) {
// Replace `-` with `.pre.` as per ruby gem parsing
// See https://github.com/rubygems/rubygems/blob/master/lib/rubygems/version.rb#L229
return versionString.replace(/-/g, '.pre.');
}
60 changes: 60 additions & 0 deletions src/updaters/ruby/gemfile-lock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {DefaultUpdater, UpdateOptions} from '../default';
import {RUBY_VERSION_REGEX, resolveRubyGemfileLockVersion} from './common';

export interface GemfileLockOptions extends UpdateOptions {
gemName: string;
}

/**
* Builds a regex matching a gem version in a Gemfile.lock file.
* @example
* rails (7.0.1)
* rails (7.0.1.alpha1)
*/
export function buildGemfileLockVersionRegex(gemName: string) {
return new RegExp(`s*${gemName} \\(${RUBY_VERSION_REGEX.source}\\)`);
}

/**
* Updates a Gemfile.lock files which is expected to have a local path version string.
*/
export class GemfileLock extends DefaultUpdater {
gemName: string;

constructor(options: GemfileLockOptions) {
super(options);
this.gemName = options.gemName;
}

/**
* Given initial file contents, return updated contents.
* @param {string} content The initial content
* @returns {string} The updated content
*/
updateContent(content: string): string {
// Bundler will convert 1.0.0-alpha1 to 1.0.0.pre.alpha1, so we need to
// do the same here.
const versionString = resolveRubyGemfileLockVersion(
this.version.toString()
);

return content.replace(
buildGemfileLockVersionRegex(this.gemName),
`${this.gemName} (${versionString})`
);
}
}
9 changes: 7 additions & 2 deletions src/updaters/ruby/version-rb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
// limitations under the License.

import {DefaultUpdater} from '../default';
import {RUBY_VERSION_REGEX, stringifyRubyVersion} from './common';

const RUBY_VERSION_RB_REGEX = new RegExp(
`(["'])(${RUBY_VERSION_REGEX.source})(["'])`
);

/**
* Updates a versions.rb file which is expected to have a version string.
Expand All @@ -25,8 +30,8 @@ export class VersionRB extends DefaultUpdater {
*/
updateContent(content: string): string {
return content.replace(
/(["'])[0-9]+\.[0-9]+\.[0-9]+(-\w+)?["']/,
`$1${this.version}$1`
RUBY_VERSION_RB_REGEX,
`$1${stringifyRubyVersion(this.version)}$1`
);
}
}
7 changes: 5 additions & 2 deletions test/strategies/ruby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {TagName} from '../../src/util/tag-name';
import {Version} from '../../src/version';
import {Changelog} from '../../src/updaters/changelog';
import {VersionRB} from '../../src/updaters/ruby/version-rb';
import {GemfileLock} from '../../src/updaters/ruby/gemfile-lock';
import {PullRequestBody} from '../../src/util/pull-request-body';

const sandbox = sinon.createSandbox();
Expand Down Expand Up @@ -96,9 +97,10 @@ describe('Ruby', () => {
latestRelease
);
const updates = release!.updates;
expect(updates).lengthOf(2);
expect(updates).lengthOf(3);
assertHasUpdate(updates, 'CHANGELOG.md', Changelog);
assertHasUpdate(updates, 'lib/google/cloud/automl/version.rb', VersionRB);
assertHasUpdate(updates, 'Gemfile.lock', GemfileLock);
});
it('allows overriding version file', async () => {
const strategy = new Ruby({
Expand All @@ -113,9 +115,10 @@ describe('Ruby', () => {
latestRelease
);
const updates = release!.updates;
expect(updates).lengthOf(2);
expect(updates).lengthOf(3);
assertHasUpdate(updates, 'CHANGELOG.md', Changelog);
assertHasUpdate(updates, 'lib/foo/version.rb', VersionRB);
assertHasUpdate(updates, 'Gemfile.lock', GemfileLock);
});
// TODO: add tests for tag separator
// TODO: add tests for post-processing commit messages
Expand Down

0 comments on commit 9baf736

Please sign in to comment.