rails-diff 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 282714f27151e8626a70a7a5d583dd16d5f623b5bd6dc5a18b0bd3799972c9b0
4
- data.tar.gz: 97096cefae2cadbd8708d19cb1d6bfa448a3f324d85b1fa406196de6cc776494
3
+ metadata.gz: f41070619ee42e4f9de3554fc4510a4801a65bc70c2a223a1547a2f46f6df746
4
+ data.tar.gz: c320a4ea5aaa7bb0a90894c721d4ff72cc83ae19aab47ddf9dce209bfc5be867
5
5
  SHA512:
6
- metadata.gz: 3aac33eb6514f976ef2d69b98b9a9afcbaf488b4dd10966f7895296cbec5035800851c6ce274a1bbd4dfdd09bda75c95474bb4aa1c441611ff52513e4acc4ccd
7
- data.tar.gz: 12c2327650881b2de7fcecd7b154106b1445794d8088b12f23f6c0fa2a528d9026c434a4ed6e3b056ccf1bc5d037a7e4c7a958ffa77f74b1ed039fd319e13e3e
6
+ metadata.gz: 7ed84dbd16eb5310e4d04c5a6cfa770c3dcbf4c217eb4647e8bde45108b8ffb32a2846e964a1d68ab74b85ee8132006f05d0b6e0bbf1bcf8bec1a5dddeb39ca6
7
+ data.tar.gz: 6b876207ceb2d4f40c7a02439ff50f127b0d81bf740816f71c00c253400c062170c6ccf99230fd213817c7871045c50f89a8ad70d24eea6bd8068e06bea2b51b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.8.0] - 2026-03-26
4
+
5
+ - `--ref` now accepts `rails --version` output (e.g., `--ref "Rails 7.2.3"` is converted to `v7.2.3`).
6
+
7
+ ## [0.7.0] - 2026-03-17
8
+
9
+ - Replace `diffy` with `difftastic` for better diff output with syntax highlighting.
10
+ - Add `--ref` option to compare against a specific tag, branch, or commit SHA (`--commit` is kept as an alias).
11
+ - [BUGFIX] `--commit` (now `--ref`) failed with shallow clones when the ref wasn't locally available.
12
+
3
13
  ## [0.6.0] - 2025-07-25
4
14
 
5
15
  - Add `--only` option to only include specific files or directories in the diff.
@@ -70,6 +80,8 @@ M## [0.1.1] - 2025-02-21
70
80
 
71
81
  - Initial release
72
82
 
83
+ [0.8.0]: https://github.com/matheusrich/rails-diff/releases/tag/v0.8.0
84
+ [0.7.0]: https://github.com/matheusrich/rails-diff/releases/tag/v0.7.0
73
85
  [0.6.0]: https://github.com/matheusrich/rails-diff/releases/tag/v0.6.0
74
86
  [0.5.0]: https://github.com/matheusrich/rails-diff/releases/tag/v0.5.0
75
87
  [0.4.1]: https://github.com/matheusrich/rails-diff/releases/tag/v0.4.1
data/README.md CHANGED
@@ -41,8 +41,8 @@ rails-diff file Dockerfile --clear-cache
41
41
  # Fail if there are differences (useful for CI)
42
42
  rails-diff file Dockerfile --fail-on-diff
43
43
 
44
- # Compare a specific commit
45
- rails-diff file Dockerfile --commit 7df1b8
44
+ # Compare a specific ref (tag, branch, or commit SHA)
45
+ rails-diff file Dockerfile --ref v8.0.0
46
46
  ```
47
47
 
48
48
  ### Compare generator files
@@ -63,8 +63,8 @@ rails-diff generated scaffold Post --skip app/views app/helpers
63
63
  # Fail if there are differences (useful for CI)
64
64
  rails-diff generated scaffold Post --fail-on-diff
65
65
 
66
- # Compare a specific commit
67
- rails-diff generated authentication --commit 7df1b8
66
+ # Compare a specific ref (tag, branch, or commit SHA)
67
+ rails-diff generated authentication --ref v8.0.0
68
68
  ```
69
69
 
70
70
  ### Compare dotfiles (configuration files)
@@ -74,16 +74,45 @@ rails-diff generated authentication --commit 7df1b8
74
74
  rails-diff dotfiles
75
75
  ```
76
76
 
77
+ ### Compare infrastructure files
78
+
79
+ Compare all Rails-generated infrastructure files at once. This includes everything except `app/` and `lib/` (your application code): `bin/`, `config/`, `db/`, `public/`, `Dockerfile`, `Gemfile`, `Rakefile`, dotfiles, and more.
80
+
81
+ ```bash
82
+ # Compare all infrastructure files
83
+ rails-diff infra
84
+
85
+ # Compare only specific directories
86
+ rails-diff infra --only bin config
87
+
88
+ # Skip additional directories
89
+ rails-diff infra --skip db
90
+
91
+ # Fail if there are differences (useful for CI)
92
+ rails-diff infra --fail-on-diff
93
+ ```
94
+
77
95
  ### Global Options
78
96
 
97
+ These options can be used with any of the commands above.
98
+
79
99
  #### --fail-on-diff
80
100
 
81
101
  If this option is specified, the command will exit with a non-zero status code if there are any differences between your files and the generated ones. This can be particularly useful when using the gem in Continuous Integration (CI) environments.
82
102
 
83
- #### --commit <commit_hash>
103
+ #### --ref <ref>
104
+
105
+ Specify a tag, branch, or commit SHA to compare against. If not provided, the
106
+ latest commit on main will be used by default. `--commit` is kept as an alias.
107
+
108
+ You can also pass `rails --version` output directly:
109
+
110
+ ```bash
111
+ rails-diff file Dockerfile --ref "$(rails --version)"
112
+ ```
84
113
 
85
- Specify the commit hash you want to compare against. If not provided, the latest
86
- commit on main will be used by default.
114
+ > [!NOTE]
115
+ > When using a commit SHA, the full 40-character SHA is required (short SHAs are not supported).
87
116
 
88
117
  #### --new-app-options <options>
89
118
 
@@ -148,7 +177,7 @@ The gem caches the generated Rails application to avoid regenerating it on every
148
177
  - The cache directory doesn't exist (or is cleared with the `--clear-cache` option)
149
178
  - You use `--new-app-options` with different options
150
179
  - You change your `~/.railsrc` file
151
- - You use `--commit` with a different commit
180
+ - You use `--ref` with a different ref
152
181
 
153
182
  You can also force clear the cache by using the `--no-cache` option (or its alias `--clear-cache`) with any command.
154
183
 
@@ -5,7 +5,7 @@ module Rails
5
5
  class CLI < Thor
6
6
  class_option :no_cache, type: :boolean, desc: "Clear cache before running", aliases: ["--clear-cache"]
7
7
  class_option :fail_on_diff, type: :boolean, desc: "Fail if there are differences"
8
- class_option :commit, type: :string, desc: "Compare against a specific commit"
8
+ class_option :ref, type: :string, desc: "Compare against a specific ref (tag, branch, or commit SHA)", aliases: ["--commit"]
9
9
  class_option :new_app_options, type: :string, desc: "Options to pass to the rails new command"
10
10
  class_option :debug, type: :boolean, desc: "Print debug information", aliases: ["-d"]
11
11
 
@@ -19,7 +19,7 @@ module Rails
19
19
  diff = Rails::Diff.file(
20
20
  *files,
21
21
  no_cache: options[:no_cache],
22
- commit: options[:commit],
22
+ ref: options[:ref],
23
23
  new_app_options: options[:new_app_options]
24
24
  )
25
25
  return if diff.empty?
@@ -45,7 +45,24 @@ module Rails
45
45
  no_cache: options[:no_cache],
46
46
  skip: options[:skip],
47
47
  only: options[:only],
48
- commit: options[:commit],
48
+ ref: options[:ref],
49
+ new_app_options: options[:new_app_options]
50
+ )
51
+ return if diff.empty?
52
+
53
+ options[:fail_on_diff] ? abort(diff) : puts(diff)
54
+ end
55
+
56
+ desc "infra", "Compare non-application files in your repository (everything except app/ and lib/) with the ones generated by Rails"
57
+ option :skip, type: :array, desc: "Additional files or directories to skip", aliases: ["-s"], default: []
58
+ option :only, type: :array, desc: "Only include specific files or directories", aliases: ["-o"], default: []
59
+ def infra
60
+ ENV["DEBUG"] = "true" if options[:debug]
61
+ diff = Rails::Diff.infra(
62
+ no_cache: options[:no_cache],
63
+ skip: options[:skip],
64
+ only: options[:only],
65
+ ref: options[:ref],
49
66
  new_app_options: options[:new_app_options]
50
67
  )
51
68
  return if diff.empty?
@@ -2,29 +2,20 @@
2
2
 
3
3
  module Rails
4
4
  module Diff
5
- class FileTracker
6
- def initialize(base_dir, skip = [], only = [])
7
- @base_dir = base_dir
8
- @skip = skip
9
- @only = only
10
- end
5
+ module FileTracker
6
+ DEFAULT_EXCLUSIONS = %w[.git tmp log test].freeze
11
7
 
12
- def new_files
13
- files_before = list_files(@base_dir)
8
+ def self.new_files(base_dir, only:, skip: [])
9
+ files_before = list_files(base_dir)
14
10
  yield
15
- files_after = list_files(@base_dir, @skip, @only)
11
+ files_after = list_files(base_dir, skip:, only:)
16
12
  files_after - files_before
17
13
  end
18
14
 
19
- private
20
-
21
- def list_files(dir, skip = [], only = [])
15
+ def self.list_files(dir, skip: [], only: [], exclusions: DEFAULT_EXCLUSIONS)
22
16
  files = Dir.glob("#{dir}/**/*", File::FNM_DOTMATCH).reject do |it|
23
17
  File.directory?(it) ||
24
- it.start_with?("#{dir}/.git") ||
25
- it.start_with?("#{dir}/tmp") ||
26
- it.start_with?("#{dir}/log") ||
27
- it.start_with?("#{dir}/test") ||
18
+ exclusions.any? { |e| it.start_with?("#{dir}/#{e}") } ||
28
19
  skip.any? { |s| it.start_with?("#{dir}/#{s}") }
29
20
  end
30
21
 
@@ -3,12 +3,10 @@ require "digest"
3
3
  module Rails
4
4
  module Diff
5
5
  class RailsAppGenerator
6
- RAILSRC_PATH = "#{ENV["HOME"]}/.railsrc"
7
-
8
- def initialize(commit: nil, new_app_options: nil, no_cache: false, logger: Logger, cache_dir: Rails::Diff::CACHE_DIR)
6
+ def initialize(ref: nil, new_app_options: nil, no_cache: false, logger: Logger, cache_dir: Rails::Diff::CACHE_DIR, rails_repo: RailsRepo.new(logger:, cache_dir:))
9
7
  @new_app_options = new_app_options.to_s.split
10
- @rails_repo = RailsRepo.new(logger:, cache_dir:)
11
- @commit = commit
8
+ @rails_repo = rails_repo
9
+ @ref = normalize_ref(ref)
12
10
  @logger = logger
13
11
  @cache_dir = cache_dir
14
12
  clear_cache if no_cache
@@ -44,8 +42,8 @@ module Rails
44
42
  Shell.run!("bin/rails", "destroy", generator_name, *args, logger:)
45
43
  logger.info "Running generator: rails generate #{generator_name} #{args.join(" ")}"
46
44
 
47
- FileTracker.new(template_app_path, skip, only)
48
- .new_files { Shell.run!("bin/rails", "generate", generator_name, *args, logger:) }
45
+ FileTracker
46
+ .new_files(template_app_path, skip:, only:) { Shell.run!("bin/rails", "generate", generator_name, *args, logger:) }
49
47
  .map { |it| it.delete_prefix("#{template_app_path}/") }
50
48
  end
51
49
  end
@@ -54,12 +52,22 @@ module Rails
54
52
 
55
53
  attr_reader :new_app_options, :rails_repo, :logger, :cache_dir
56
54
 
57
- def commit = @commit ||= rails_repo.latest_commit
55
+ def normalize_ref(ref)
56
+ if ref&.start_with?("Rails ")
57
+ "v#{ref.delete_prefix("Rails ")}"
58
+ else
59
+ ref
60
+ end
61
+ end
62
+
63
+ def ref = @ref ||= rails_repo.latest_commit
64
+
65
+ def rails_cache_dir_key = "rails-#{ref.first(10)}"
58
66
 
59
- def rails_cache_dir_key = "rails-#{commit.first(10)}"
67
+ def railsrc_path = "#{ENV["HOME"]}/.railsrc"
60
68
 
61
69
  def railsrc_options
62
- @railsrc_options ||= File.exist?(RAILSRC_PATH) ? File.readlines(RAILSRC_PATH) : []
70
+ @railsrc_options ||= File.exist?(railsrc_path) ? File.readlines(railsrc_path) : []
63
71
  end
64
72
 
65
73
  def app_name = @app_name ||= File.basename(Dir.pwd)
@@ -69,19 +77,19 @@ module Rails
69
77
  end
70
78
 
71
79
  def create_new_rails_app
72
- checkout_rails_commit
80
+ checkout_rails_ref
73
81
  generate_app
74
82
  end
75
83
 
76
84
  def generate_app
77
85
  rails_repo.install_dependencies
78
86
  if railsrc_options.any?
79
- logger.info "Using default options from #{RAILSRC_PATH}:\n\t > #{railsrc_options.join(" ")}"
87
+ logger.info "Using default options from #{railsrc_path}:\n\t > #{railsrc_options.join(" ")}"
80
88
  end
81
89
  rails_repo.new_app(template_app_path, rails_new_options)
82
90
  end
83
91
 
84
- def checkout_rails_commit = rails_repo.checkout(commit)
92
+ def checkout_rails_ref = rails_repo.checkout(ref)
85
93
 
86
94
  def rails_new_options
87
95
  @rails_new_options ||= (new_app_options + railsrc_options).compact
@@ -9,10 +9,13 @@ module Rails
9
9
  @rails_repo = rails_repo
10
10
  end
11
11
 
12
- def checkout(commit)
12
+ def checkout(ref)
13
13
  on_rails_dir do
14
- logger.info "Checking out Rails (at commit #{commit[0..6]})"
15
- Shell.run!("git", "checkout", commit, logger:)
14
+ logger.info "Checking out Rails (at #{ref})"
15
+ unless Shell.run!("git", "checkout", ref, abort: false, logger:)
16
+ Shell.run!("git", "fetch", "--depth", "1", "origin", ref, logger:)
17
+ Shell.run!("git", "checkout", "FETCH_HEAD", logger:)
18
+ end
16
19
  end
17
20
  end
18
21
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rails
4
4
  module Diff
5
- VERSION = "0.6.1"
5
+ VERSION = "0.8.0"
6
6
  end
7
7
  end
data/lib/rails/diff.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails"
4
- require "diffy"
4
+ require "difftastic"
5
5
  require "fileutils"
6
6
  require_relative "diff/cli"
7
7
  require_relative "diff/file_tracker"
@@ -16,48 +16,59 @@ module Rails
16
16
  CACHE_DIR = File.expand_path("#{ENV["HOME"]}/.rails-diff/cache")
17
17
 
18
18
  class << self
19
- def file(*files, no_cache: false, commit: nil, new_app_options: nil)
20
- app_generator = RailsAppGenerator.new(commit:, new_app_options:, no_cache:)
19
+ def file(*files, no_cache: false, ref: nil, new_app_options: nil, app_generator: RailsAppGenerator.new(ref:, new_app_options:, no_cache:), differ_class: Difftastic::Differ)
21
20
  app_generator.create_template_app
22
21
 
23
22
  files
24
- .filter_map { |it| diff_with_header(it, app_generator.template_app_path) }
23
+ .filter_map { |it| diff_with_header(it, app_generator.template_app_path, differ_class:) }
25
24
  .join("\n")
26
25
  end
27
26
 
28
- def generated(generator_name, *args, no_cache: false, skip: [], only: [], commit: nil, new_app_options: nil)
29
- app_generator = RailsAppGenerator.new(commit:, new_app_options:, no_cache:)
27
+ def generated(generator_name, *args, no_cache: false, skip: [], only: [], ref: nil, new_app_options: nil, app_generator: RailsAppGenerator.new(ref:, new_app_options:, no_cache:), differ_class: Difftastic::Differ)
30
28
  app_generator.create_template_app
31
29
  app_generator.install_app_dependencies
32
30
 
33
31
  app_generator.run_generator(generator_name, *args, skip, only)
34
- .map { |it| diff_with_header(it, app_generator.template_app_path) }
32
+ .filter_map { |it| diff_with_header(it, app_generator.template_app_path, differ_class:) }
33
+ .join("\n\n")
34
+ end
35
+
36
+ def infra(no_cache: false, skip: [], only: [], ref: nil, new_app_options: nil, app_generator: RailsAppGenerator.new(ref:, new_app_options:, no_cache:), differ_class: Difftastic::Differ)
37
+ app_generator.create_template_app
38
+
39
+ default_skip = %w[app lib]
40
+ effective_skip = (default_skip + skip).uniq
41
+
42
+ FileTracker.list_files(app_generator.template_app_path, skip: effective_skip, only:)
43
+ .map { |f| f.delete_prefix("#{app_generator.template_app_path}/") }
44
+ .filter_map { |it| diff_with_header(it, app_generator.template_app_path, differ_class:) }
35
45
  .join("\n\n")
36
46
  end
37
47
 
38
48
  private
39
49
 
40
- def diff_with_header(file, template_app_path)
41
- diff = diff_file(file, template_app_path)
50
+ def diff_with_header(file, template_app_path, differ_class:)
51
+ diff = diff_file(file, template_app_path, differ_class:)
42
52
  return if diff.empty?
43
53
 
44
54
  header = "#{file} diff:"
45
55
  [header, "=" * header.size, diff].join("\n")
46
56
  end
47
57
 
48
- def diff_file(file, template_app_path)
58
+ def diff_file(file, template_app_path, differ_class:)
49
59
  rails_file = File.join(template_app_path, file)
50
60
  repo_file = File.join(Dir.pwd, file)
51
61
 
52
62
  return "File not found in the Rails template" unless File.exist?(rails_file)
53
63
  return "File not found in your repository" unless File.exist?(repo_file)
54
64
 
55
- Diffy::Diff.new(
56
- rails_file,
57
- repo_file,
58
- context: 2,
59
- source: "files"
60
- ).to_s(:color).chomp
65
+ differ = differ_class.new(
66
+ color: :always,
67
+ left_label: "Rails File (#{file})",
68
+ right_label: "Repo File (#{file})"
69
+ )
70
+
71
+ differ.diff_files(rails_file, repo_file).chomp
61
72
  end
62
73
  end
63
74
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matheus Richard
@@ -24,19 +24,19 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: '7.0'
26
26
  - !ruby/object:Gem::Dependency
27
- name: diffy
27
+ name: difftastic
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '3.4'
32
+ version: '0.8'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '3.4'
39
+ version: '0.8'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: thor
42
42
  requirement: !ruby/object:Gem::Requirement