From a6683d767a50734a227fd7b43b5dc38a1e8c8b4d Mon Sep 17 00:00:00 2001 From: Andrew Thauer <6507159+andrewthauer@users.noreply.github.com> Date: Sat, 31 Dec 2022 12:56:34 -0500 Subject: [PATCH] feat(ruby): add Gemfile.Lock updater 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 `-`. --- __snapshots__/gemfile-lock.js | 127 ++++++++++++++++++ __snapshots__/version-rb.js | 25 ++++ src/strategies/ruby.ts | 11 ++ src/updaters/ruby/common.ts | 52 +++++++ src/updaters/ruby/gemfile-lock.ts | 60 +++++++++ src/updaters/ruby/version-rb.ts | 9 +- test/strategies/ruby.ts | 7 +- test/updaters/fixtures/Gemfile.lock | 60 +++++++++ .../fixtures/version-with-prerelease.rb | 21 +++ test/updaters/gemfile-lock.ts | 93 +++++++++++++ test/updaters/ruby/common.ts | 115 ++++++++++++++++ test/updaters/version-rb.ts | 44 ++++++ 12 files changed, 620 insertions(+), 4 deletions(-) create mode 100644 __snapshots__/gemfile-lock.js create mode 100644 src/updaters/ruby/common.ts create mode 100644 src/updaters/ruby/gemfile-lock.ts create mode 100644 test/updaters/fixtures/Gemfile.lock create mode 100644 test/updaters/fixtures/version-with-prerelease.rb create mode 100644 test/updaters/gemfile-lock.ts create mode 100644 test/updaters/ruby/common.ts diff --git a/__snapshots__/gemfile-lock.js b/__snapshots__/gemfile-lock.js new file mode 100644 index 000000000..08b3c1fdb --- /dev/null +++ b/__snapshots__/gemfile-lock.js @@ -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 + +` diff --git a/__snapshots__/version-rb.js b/__snapshots__/version-rb.js index b4f7bce5a..dddb8c3bd 100644 --- a/__snapshots__/version-rb.js +++ b/__snapshots__/version-rb.js @@ -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 # diff --git a/src/strategies/ruby.ts b/src/strategies/ruby.ts index c08eaddbc..9b3581060 100644 --- a/src/strategies/ruby.ts +++ b/src/strategies/ruby.ts @@ -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'; @@ -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; } diff --git a/src/updaters/ruby/common.ts b/src/updaters/ruby/common.ts new file mode 100644 index 000000000..2ac0f4588 --- /dev/null +++ b/src/updaters/ruby/common.ts @@ -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.'); +} diff --git a/src/updaters/ruby/gemfile-lock.ts b/src/updaters/ruby/gemfile-lock.ts new file mode 100644 index 000000000..874698231 --- /dev/null +++ b/src/updaters/ruby/gemfile-lock.ts @@ -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})` + ); + } +} diff --git a/src/updaters/ruby/version-rb.ts b/src/updaters/ruby/version-rb.ts index da48ef2e1..6e5535525 100644 --- a/src/updaters/ruby/version-rb.ts +++ b/src/updaters/ruby/version-rb.ts @@ -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. @@ -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` ); } } diff --git a/test/strategies/ruby.ts b/test/strategies/ruby.ts index 667fd2fd6..c10704ee6 100644 --- a/test/strategies/ruby.ts +++ b/test/strategies/ruby.ts @@ -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(); @@ -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({ @@ -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 diff --git a/test/updaters/fixtures/Gemfile.lock b/test/updaters/fixtures/Gemfile.lock new file mode 100644 index 000000000..6a4703891 --- /dev/null +++ b/test/updaters/fixtures/Gemfile.lock @@ -0,0 +1,60 @@ +PATH + remote: . + specs: + foo (0.1.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 diff --git a/test/updaters/fixtures/version-with-prerelease.rb b/test/updaters/fixtures/version-with-prerelease.rb new file mode 100644 index 000000000..014e71153 --- /dev/null +++ b/test/updaters/fixtures/version-with-prerelease.rb @@ -0,0 +1,21 @@ +# 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 diff --git a/test/updaters/gemfile-lock.ts b/test/updaters/gemfile-lock.ts new file mode 100644 index 000000000..5875c1460 --- /dev/null +++ b/test/updaters/gemfile-lock.ts @@ -0,0 +1,93 @@ +// 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 {readFileSync} from 'fs'; +import {resolve} from 'path'; +import * as snapshot from 'snap-shot-it'; +import {describe, it} from 'mocha'; +import {expect} from 'chai'; +import {GemfileLock} from '../../src/updaters/ruby/gemfile-lock'; +import {Version} from '../../src/version'; + +const fixturesPath = './test/updaters/fixtures'; + +describe('Gemfile.lock', () => { + describe('updateContent', () => { + // gemName, newVersion, existingContent, expected, shouldUpdate, description + // prettier-ignore + const testTable: [string, string, string, string, boolean, string][] = [ + ['foo', '0.2.0', 'foo (0.1.0)', 'foo (0.2.0)', true, 'minor'], + ['foo', '0.2.1', 'foo (0.2.0)', 'foo (0.2.1)', true, 'patch'], + ['foo', '0.2.11', 'foo (0.2.1)', 'foo (0.2.11)', true, 'long patch'], + ['foo', '0.3.0-alpha1', 'foo (0.2.0)', 'foo (0.3.0.pre.alpha1)', true, 'prerelease'], + ['foo', '0.3.0-alpha2', 'foo (0.3.0.pre.alpha1)', 'foo (0.3.0.pre.alpha2)', true, 'prerelease bump'], + ['foo', '1.0.0-beta', 'foo (0.3.0.pre.alpha2)', 'foo (1.0.0.pre.beta)', true, 'beta'], + ['foo', '1.0.0', 'foo (1.0.0.beta)', 'foo (1.0.0)', true, 'major'], + ['foo', '2.0.22', 'foo (1.0.0)', 'foo (2.0.22)', true, 'major bump with long patch'], + ['foo', '2.0.22', 'foo (1.0.0-alpha1)', 'foo (2.0.22)', true, 'update semantic version'], + ['foo', '1.0.0', 'something', 'something', false, 'text to ignore'], + ['foo', '1.0.0', 'foo 1.0.0', 'foo 1.0.0', false, 'no parantheses around version'], + ['foo', '1.0.0', 'barfoo (1.0.0)', 'barfoo (1.0.0)', false, 'prefixed gem name'], + ['foo', '1.0.0', 'foobar (0.1.0)', 'foobar (0.1.0)', false, 'suffixed gem name'], + ]; + + testTable.forEach( + ([ + gemName, + newVersion, + existingContent, + expected, + shouldUpdate, + description, + ]) => { + it(`should ${ + shouldUpdate ? 'update' : 'not update' + } for ${description}`, () => { + const version = new GemfileLock({ + version: Version.parse(newVersion), + gemName, + }); + const result = version.updateContent(existingContent); + expect(result).to.equal(expected); + }); + } + ); + + it('updates version in Gemfile.lock', async () => { + const oldContent = readFileSync( + resolve(fixturesPath, './Gemfile.lock'), + 'utf8' + ).replace(/\r\n/g, '\n'); + const version = new GemfileLock({ + version: Version.parse('0.2.0'), + gemName: 'foo', + }); + const newContent = version.updateContent(oldContent); + snapshot(newContent); + }); + + it('updates prerelease in Gemfile.lock', async () => { + const oldContent = readFileSync( + resolve(fixturesPath, './Gemfile.lock'), + 'utf8' + ).replace(/\r\n/g, '\n'); + const version = new GemfileLock({ + version: Version.parse('0.2.0-alpha'), + gemName: 'foo', + }); + const newContent = version.updateContent(oldContent); + snapshot(newContent); + }); + }); +}); diff --git a/test/updaters/ruby/common.ts b/test/updaters/ruby/common.ts new file mode 100644 index 000000000..1ccaf4544 --- /dev/null +++ b/test/updaters/ruby/common.ts @@ -0,0 +1,115 @@ +// 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 {describe, it} from 'mocha'; +import {expect} from 'chai'; +import { + resolveRubyGemfileLockVersion, + stringifyRubyVersion, +} from '../../../src/updaters/ruby/common'; +import {Version} from '../../../src/version'; + +describe('ruby-common', () => { + describe('resolveRubyGemfileLockVersion', () => { + // input, expected + const testTable: [string, string][] = [ + ['0.0.0', '0.0.0'], + ['1.2.3', '1.2.3'], + ['15.10.22', '15.10.22'], + ['1.0.0-alpha', '1.0.0.pre.alpha'], + ['1.0.0-alpha1', '1.0.0.pre.alpha1'], + ['2.0.0-rc1', '2.0.0.pre.rc1'], + ]; + + testTable.forEach(([input, expected]) => { + it(`${input} should resolve to ${expected}`, () => { + expect( + resolveRubyGemfileLockVersion(Version.parse(input).toString()) + ).to.equal(expected); + }); + }); + }); + + describe('stringifyRubyVersion', () => { + // input, expected + const testTable: [string, string][] = [ + ['0.2.0', '0.2.0'], + ['1.2.3', '1.2.3'], + ['1.2.10', '1.2.10'], + ['15.10.22', '15.10.22'], + ['1.0.0-alpha', '1.0.0-alpha'], + ['1.0.0-alpha1', '1.0.0-alpha1'], + ['2.0.0-rc1', '2.0.0-rc1'], + ]; + + testTable.forEach(([input, expected]) => { + it(`${input} should equal ${expected}`, () => { + expect(stringifyRubyVersion(Version.parse(input))).to.equal(expected); + }); + }); + + describe('combined with resolve resolveRubyGemfileLockVersion', () => { + // input, expected + const testTable: [string, string][] = [ + ['0.2.0', '0.2.0'], + ['1.2.3', '1.2.3'], + ['1.2.10', '1.2.10'], + ['15.10.22', '15.10.22'], + ['1.0.0-alpha', '1.0.0.pre.alpha'], + ['1.0.0-alpha1', '1.0.0.pre.alpha1'], + ['2.0.0-rc1', '2.0.0.pre.rc1'], + ]; + + testTable.forEach(([input, expected]) => { + it(`${input} combined with resolveRubyGemfileLockVersion should equal ${expected}`, () => { + const versionString = stringifyRubyVersion(Version.parse(input)); + expect(resolveRubyGemfileLockVersion(versionString)).to.equal( + expected + ); + }); + }); + }); + + describe('with dot prelease seperator', () => { + const testTable: [string, string][] = [ + ['0.2.0', '0.2.0'], + ['1.2.3', '1.2.3'], + ['1.2.10', '1.2.10'], + ['15.10.22', '15.10.22'], + ['1.0.0-alpha', '1.0.0.alpha'], + ['1.0.0-alpha1', '1.0.0.alpha1'], + ['2.0.0-beta', '2.0.0.beta'], + ['2.0.0-rc1', '2.0.0.rc1'], + ]; + + testTable.forEach(([input, expected]) => { + it(`${input} should equal ${expected}`, () => { + expect(stringifyRubyVersion(Version.parse(input), true)).to.equal( + expected + ); + }); + + it(`${input} combined with resolveRubyGemfileLockVersion should equal ${expected}`, () => { + const versionString = stringifyRubyVersion( + Version.parse(input), + true + ); + expect(resolveRubyGemfileLockVersion(versionString)).to.equal( + expected + ); + }); + }); + }); + }); +}); diff --git a/test/updaters/version-rb.ts b/test/updaters/version-rb.ts index 86fe18ad5..ed5e87ec5 100644 --- a/test/updaters/version-rb.ts +++ b/test/updaters/version-rb.ts @@ -16,6 +16,7 @@ import {readFileSync} from 'fs'; import {resolve} from 'path'; import * as snapshot from 'snap-shot-it'; import {describe, it} from 'mocha'; +import {expect} from 'chai'; import {VersionRB} from '../../src/updaters/ruby/version-rb'; import {Version} from '../../src/version'; @@ -23,6 +24,37 @@ const fixturesPath = './test/updaters/fixtures'; describe('version.rb', () => { describe('updateContent', () => { + // newVersion, existingContent, expected, shouldUpdate, description + const testTable: [string, string, string, boolean, string][] = [ + ['0.2.0', "'0.1.0'", "'0.2.0'", true, 'single quotes'], + ['0.2.0', '"0.1.0"', '"0.2.0"', true, 'double quotes'], + ['0.2.0', '"0.1.0"', '"0.2.0"', true, 'minor'], + ['0.2.1', '"0.2.0"', '"0.2.1"', true, 'patch'], + ['0.2.11', '"0.2.10"', '"0.2.11"', true, 'long patch'], + ['1.0.0-alpha1', '"0.9.0"', '"1.0.0-alpha1"', true, 'prerelease'], + ['1.0.0-beta', '"1.0.0-alpha1"', '"1.0.0-beta"', true, 'prerelease bump'], + ['1.0.0', '"1.0.0.beta"', '"1.0.0"', true, 'major'], + ['1.0.1', '"1.0.0"', '"1.0.1"', true, 'major patch'], + ['1.0.0', '"1.0"', '"1.0"', false, 'ignored'], + ['1.0.0', 'something', 'something', false, 'random text'], + ['1.0.0', "'0.1'", "'0.1'", false, 'invalid version single quoted'], + ['1.0.0', '"0.1"', '"0.1"', false, 'invalid version double quoted'], + ]; + + testTable.forEach( + ([newVersion, existingContent, expected, shouldUpdate, description]) => { + it(`should ${ + shouldUpdate ? 'update' : 'not update' + } for ${description}`, () => { + const version = new VersionRB({ + version: Version.parse(newVersion), + }); + const result = version.updateContent(existingContent); + expect(result).to.equal(expected); + }); + } + ); + it('updates version in version.rb', async () => { const oldContent = readFileSync( resolve(fixturesPath, './version.rb'), @@ -60,5 +92,17 @@ describe('version.rb', () => { const newContent = version.updateContent(oldContent); snapshot(newContent); }); + + it('updates prerelease versions in version.rb', async () => { + const oldContent = readFileSync( + resolve(fixturesPath, './version-with-prerelease.rb'), + 'utf8' + ).replace(/\r\n/g, '\n'); + const version = new VersionRB({ + version: Version.parse('10.0.0-alpha1'), + }); + const newContent = version.updateContent(oldContent); + snapshot(newContent); + }); }); });