Skip to content

Commit

Permalink
Ensure consistent versions of package and gem (shakacode#51)
Browse files Browse the repository at this point in the history
* Use --strict and --exact flags in generator and instructions
* Add version check
* Make behaviour opt-in
* Emit warning if check disabled
  • Loading branch information
Tom Dracz committed Feb 21, 2022
1 parent 5c9707e commit 2b70a6f
Show file tree
Hide file tree
Showing 18 changed files with 560 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Changes since last non-beta release.

### Added
- Rewrite webpack module rules as regular expressions. Allows for easy iteration during config customization. [PR 60](https://github.com/shakacode/shakapacker/pull/60) by [blnoonan](https://github.com/blnoonan)
- Initialization check to ensure shakapacker gem and NPM package version are consistent. Opt-in behaviour enabled by setting `ensure_consistent_versioning` configuration variable. [PR 51](https://github.com/shakacode/shakapacker/pull/51) by [tomdracz](https://github.com/tomdracz).

## [v6.1.1] - February 6, 2022

Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,10 @@ rails new myapp --skip-javascript

_Note, Rails 6 installs the older v5 version of webpacker unless you specify `--skip-javascript`._

Update your `Gemfile`:
Add `shakapacker` gem to your `Gemfile`:

```ruby
# Gemfile
gem 'shakapacker', '~> 6.0'
```bash
bundle add shakapacker --strict
```

Then running the following to install Webpacker:
Expand Down
3 changes: 3 additions & 0 deletions lib/install/config/webpacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ default: &default
# Select loader to use, available options are 'babel' (default), 'swc' or 'esbuild'
webpack_loader: 'babel'

# Set to true to enable check for matching versions of shakapacker gem and NPM package - will raise an error if there is a mismatch or wildcard versioning is used
ensure_consistent_versioning: false

development:
<<: *default
compile: true
Expand Down
4 changes: 2 additions & 2 deletions lib/install/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@
Dir.chdir(Rails.root) do
if Webpacker::VERSION.match?(/^[0-9]+\.[0-9]+\.[0-9]+$/)
say "Installing shakapacker@#{Webpacker::VERSION}"
results << run("yarn add shakapacker@#{Webpacker::VERSION}")
results << run("yarn add shakapacker@#{Webpacker::VERSION} --exact")
else
say "Installing shakapacker@next"
results << run("yarn add shakapacker@next")
results << run("yarn add shakapacker@next --exact")
end

package_json = File.read("#{__dir__}/../../package.json")
Expand Down
4 changes: 4 additions & 0 deletions lib/webpacker/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ def compile?
fetch(:compile)
end

def ensure_consistent_versioning?
fetch(:ensure_consistent_versioning)
end

def source_path
root_path.join(fetch(:source_path))
end
Expand Down
7 changes: 7 additions & 0 deletions lib/webpacker/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@

require "webpacker/helper"
require "webpacker/dev_server_proxy"
require "webpacker/version_checker"

class Webpacker::Engine < ::Rails::Engine
# Allows Webpacker config values to be set via Rails env config files
config.webpacker = ActiveSupport::OrderedOptions.new

initializer "webpacker.version_checker" do
if File.exist?(Webpacker::VersionChecker::NodePackageVersion.package_json_path)
Webpacker::VersionChecker.build.raise_if_gem_and_node_package_versions_differ
end
end

initializer "webpacker.proxy" do |app|
if (Webpacker.config.dev_server.present? rescue nil)
app.middleware.insert_before 0,
Expand Down
152 changes: 152 additions & 0 deletions lib/webpacker/version_checker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# frozen_string_literal: true
require "webpacker/version"

module Webpacker
class VersionChecker
attr_reader :node_package_version

MAJOR_MINOR_PATCH_VERSION_REGEX = /(\d+)\.(\d+)\.(\d+)/.freeze

def self.build
new(NodePackageVersion.build)
end

def initialize(node_package_version)
@node_package_version = node_package_version
end

def raise_if_gem_and_node_package_versions_differ
# Skip check if package is not in package.json or listed from relative path, git repo or github URL
return if node_package_version.skip_processing?

node_major_minor_patch = node_package_version.major_minor_patch
gem_major_minor_patch = gem_major_minor_patch_version
versions_match = node_major_minor_patch[0] == gem_major_minor_patch[0] &&
node_major_minor_patch[1] == gem_major_minor_patch[1] &&
node_major_minor_patch[2] == gem_major_minor_patch[2]

uses_wildcard = node_package_version.semver_wildcard?

if !Webpacker.config.ensure_consistent_versioning? && (uses_wildcard || !versions_match)
check_failed = if uses_wildcard
"Semver wildcard detected"
else
"Version mismatch detected"
end

warn <<-MSG.strip_heredoc
Webpacker::VersionChecker - #{check_failed}
You are currently not checking for consistent versions of shakapacker gem and npm package. A version mismatch or usage of semantic versioning wildcard (~ or ^) has been detected.
Version mismatch can lead to incorrect behavior and bugs. You should ensure that both the gem and npm package dependencies are locked to the same version.
You can enable the version check by setting `ensure_consistent_versioning: true` in your `webpacker.yml` file.
Checking for gem and npm package versions mismatch or wildcard will be enabled by default in the next major version of shakapacker.
MSG

return
end

raise_differing_versions_warning unless versions_match

raise_node_semver_version_warning if uses_wildcard
end

private

def common_error_msg
<<-MSG.strip_heredoc
Detected: #{node_package_version.raw}
gem: #{gem_version}
Ensure the installed version of the gem is the same as the version of
your installed node package. Do not use >= or ~> in your Gemfile for shakapacker.
Do not use ^ or ~ in your package.json for shakapacker.
Run `yarn add shakapacker --exact` in the directory containing folder node_modules.
MSG
end

def raise_differing_versions_warning
msg = "**ERROR** Webpacker: Webpacker gem and node package versions do not match\n#{common_error_msg}"
raise msg
end

def raise_node_semver_version_warning
msg = "**ERROR** Webpacker: Your node package version for shakapacker contains a "\
"^ or ~\n#{common_error_msg}"
raise msg
end

def gem_version
Webpacker::VERSION
end

def gem_major_minor_patch_version
match = gem_version.match(MAJOR_MINOR_PATCH_VERSION_REGEX)
[match[1], match[2], match[3]]
end

class NodePackageVersion
attr_reader :package_json

def self.build
new(package_json_path)
end

def self.package_json_path
Rails.root.join("package.json")
end

def initialize(package_json)
@package_json = package_json
end

def raw
parsed_package_contents = JSON.parse(package_json_contents)
parsed_package_contents.dig("dependencies", "shakapacker").to_s
end

def semver_wildcard?
raw.match(/[~^]/).present?
end

def skip_processing?
!package_specified? || relative_path? || git_url? || github_url?
end

def major_minor_patch
return if skip_processing?

match = raw.match(MAJOR_MINOR_PATCH_VERSION_REGEX)
unless match
raise "Cannot parse version number '#{raw}' (wildcard versions are not supported)"
end

[match[1], match[2], match[3]]
end

private

def package_specified?
raw.present?
end

def relative_path?
raw.match(%r{(\.\.|\Afile:///)}).present?
end

def git_url?
raw.match(%r{^git}).present?
end

def github_url?
raw.match(%r{^([\w-]+\/[\w-]+)}).present?
end

def package_json_contents
@package_json_contents ||= File.read(package_json)
end
end
end
end
12 changes: 12 additions & 0 deletions test/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,16 @@ def test_compile?
assert Webpacker.config.compile?
end
end

def test_ensure_consistent_versioning?
refute @config.ensure_consistent_versioning?

with_rails_env("development") do
assert Webpacker.config.ensure_consistent_versioning?
end

with_rails_env("test") do
refute Webpacker.config.ensure_consistent_versioning?
end
end
end
13 changes: 13 additions & 0 deletions test/fixtures/beta_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "test_app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"shakapacker": "6.0.0-beta.1"
},
"devDependencies": {
"right-pad": "^1.0.1"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/git_url_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "test_app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"shakapacker": "git://github.com/shakapacker/shakapacker.git"
},
"devDependencies": {
"right-pad": "^1.0.1"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/github_url_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "test_app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"shakapacker": "shakapacker/shakapacker#feature\/branch"
},
"devDependencies": {
"right-pad": "^1.0.1"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/normal_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "test_app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"shakapacker": "6.0.0"
},
"devDependencies": {
"right-pad": "^1.0.1"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/relative_path_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "test_app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"shakapacker": "../shakapacker"
},
"devDependencies": {
"right-pad": "^1.0.1"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/semver_caret_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "test_app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"shakapacker": "^6.0.0"
},
"devDependencies": {
"right-pad": "^1.0.1"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/semver_tilde_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "test_app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"shakapacker": "~6.0.0"
},
"devDependencies": {
"right-pad": "^1.0.1"
}
}
13 changes: 13 additions & 0 deletions test/fixtures/without_package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "test_app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"left-pad": "1.0.2"
},
"devDependencies": {
"right-pad": "^1.0.1"
}
}
1 change: 1 addition & 0 deletions test/test_app/config/webpacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ default: &default
development:
<<: *default
compile: true
ensure_consistent_versioning: true

# Reference: https://webpack.js.org/configuration/dev-server/
dev_server:
Expand Down

0 comments on commit 2b70a6f

Please sign in to comment.