From e07bafcfbf13b4f86ed5e4ff7bd5cca7ffc82ed8 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 19:06:26 +0200 Subject: [PATCH 01/72] Add Gemfile --- .gitignore | 1 + Gemfile | 3 +++ Gemfile.lock | 17 +++++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..48b8bf9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +vendor/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..d65e2a6 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'http://rubygems.org' + +gemspec diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..ea857ad --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,17 @@ +PATH + remote: . + specs: + format-staged (0.0.1) + +GEM + remote: http://rubygems.org/ + specs: + +PLATFORMS + ruby + +DEPENDENCIES + format-staged! + +BUNDLED WITH + 2.1.4 From b5a376a52b2f016869d72c309dccd2ad3bf9375e Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 19:09:47 +0200 Subject: [PATCH 02/72] Add Rubocop dependency --- .gitignore | 1 + Gemfile.lock | 21 +++++++++++++++++++++ format-staged.gemspec | 2 ++ 3 files changed, 24 insertions(+) diff --git a/.gitignore b/.gitignore index 48b8bf9..7b5c283 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ vendor/ +.bundle/ diff --git a/Gemfile.lock b/Gemfile.lock index ea857ad..c450ff2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,12 +6,33 @@ PATH GEM remote: http://rubygems.org/ specs: + ast (2.4.2) + parallel (1.22.1) + parser (3.1.2.0) + ast (~> 2.4.1) + rainbow (3.1.1) + regexp_parser (2.4.0) + rexml (3.2.5) + rubocop (1.29.1) + parallel (~> 1.10) + parser (>= 3.1.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.17.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.18.0) + parser (>= 3.1.1.0) + ruby-progressbar (1.11.0) + unicode-display_width (2.1.0) PLATFORMS ruby DEPENDENCIES format-staged! + rubocop (~> 1.29) BUNDLED WITH 2.1.4 diff --git a/format-staged.gemspec b/format-staged.gemspec index df36505..0ab79ad 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -13,4 +13,6 @@ Gem::Specification.new do |s| s.executables << 'git-format-staged' s.homepage = 'https://github.com/5sw/format-staged' s.license = 'MIT' + + s.add_development_dependency 'rubocop', '~> 1.29' end From 561a403b4ef6a04b1c819c21ed11d0024f2a9438 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 19:13:01 +0200 Subject: [PATCH 03/72] Run rubocop -a --- bin/git-format-staged | 37 ++++---- format-staged.gemspec | 10 +- lib/format-staged.rb | 174 +++++++++++++++++------------------ lib/format-staged/entry.rb | 12 +-- lib/format-staged/io.rb | 30 +++--- lib/format-staged/version.rb | 4 +- 6 files changed, 134 insertions(+), 133 deletions(-) diff --git a/bin/git-format-staged b/bin/git-format-staged index 7a35a41..a6772e5 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -3,38 +3,41 @@ require 'format-staged' require 'optparse' parameters = { - :update => true, - :write => true, - :verbose => false, + update: true, + write: true, + verbose: false } parser = OptionParser.new do |opt| opt.banner = "Usage: #{opt.program_name} [options] [patterns]" - opt.separator "" - opt.on('-f', '--formatter COMMAND', 'Shell command to format files, will run once per file. Occurrences of the placeholder `{}` will be replaced with a path to the file being formatted. (Example: "prettier --stdin-filepath \'{}\'")') do |o| + opt.separator '' + opt.on('-f', '--formatter COMMAND', + 'Shell command to format files, will run once per file. Occurrences of the placeholder `{}` will be replaced with a path to the file being formatted. (Example: "prettier --stdin-filepath \'{}\'")') do |o| parameters[:formatter] = o end - - opt.on('--[no-]update-working-tree', 'By default formatting changes made to staged file content will also be applied to working tree files via a patch. This option disables that behavior, leaving working tree files untouched.') do |value| + + opt.on('--[no-]update-working-tree', + 'By default formatting changes made to staged file content will also be applied to working tree files via a patch. This option disables that behavior, leaving working tree files untouched.') do |value| parameters[:update] = value end - - opt.on('--[no-]write', "Prevents #{opt.program_name} from modifying staged or working tree files. You can use this option to check staged changes with a linter instead of formatting. With this option stdout from the formatter command is ignored.") do |value| + + opt.on('--[no-]write', + "Prevents #{opt.program_name} from modifying staged or working tree files. You can use this option to check staged changes with a linter instead of formatting. With this option stdout from the formatter command is ignored.") do |value| parameters[:write] = value end - - opt.on("-v", "--[no-]verbose", 'Shows commands being run') do |value| + + opt.on('-v', '--[no-]verbose', 'Shows commands being run') do |value| parameters[:verbose] = value end - opt.separator "" - + opt.separator '' + opt.on_tail('-h', '--help', 'Prints this help') do puts opt exit end - - opt.on_tail('--version', "Prints the version number and exits") do + + opt.on_tail('--version', 'Prints the version number and exits') do puts FormatStaged::VERSION exit end @@ -44,8 +47,8 @@ parser.parse! parameters[:patterns] = ARGV if !parameters[:formatter] or parameters[:patterns].empty? - puts "Missing formatter or file patterns!" - + puts 'Missing formatter or file patterns!' + puts parser exit end diff --git a/format-staged.gemspec b/format-staged.gemspec index 0ab79ad..e69faed 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -1,18 +1,18 @@ -lib = File.expand_path("lib", __dir__) +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'format-staged/version' Gem::Specification.new do |s| s.name = 'format-staged' s.version = FormatStaged::VERSION - s.summary = "git format staged!" - s.description = "git format staged" - s.authors = ["Sven Weidauer"] + s.summary = 'git format staged!' + s.description = 'git format staged' + s.authors = ['Sven Weidauer'] s.email = 'sven@5sw.de' s.files = Dir['lib/**/*.rb'] s.executables << 'git-format-staged' s.homepage = 'https://github.com/5sw/format-staged' s.license = 'MIT' - + s.add_development_dependency 'rubocop', '~> 1.29' end diff --git a/lib/format-staged.rb b/lib/format-staged.rb index 69e7c48..5e18a7f 100644 --- a/lib/format-staged.rb +++ b/lib/format-staged.rb @@ -3,107 +3,107 @@ require 'format-staged/entry' require 'format-staged/io' require 'shellwords' -class FormatStaged +class FormatStaged attr_reader :formatter, :patterns, :update, :write, :verbose - - def initialize(formatter:, patterns:, update: true, write: true, verbose: true) + + def initialize(formatter:, patterns:, update: true, write: true, verbose: true) @formatter = formatter @patterns = patterns @update = update @write = write @verbose = verbose end - - def run() + + def run root = get_output('git', 'rev-parse', '--show-toplevel').first - + files = get_output('git', 'diff-index', '--cached', '--diff-filter=AM', '--no-renames', 'HEAD') - .map { |line| Entry.new(line, root: root) } - .reject { |entry| entry.symlink? } - .filter { |entry| entry.matches?(@patterns) } - - files.each do |file| - format_file(file) - end - end - - def format_file(file) - new_hash = format_object file - - return true if not write + .map { |line| Entry.new(line, root: root) } + .reject { |entry| entry.symlink? } + .filter { |entry| entry.matches?(@patterns) } - if new_hash == file.dst_hash - puts "Unchanged #{file.src_path}" - return false - end - - if object_is_empty new_hash - puts "Skipping #{file.src_path}, formatted file is empty" - return false - end + files.each do |file| + format_file(file) + end + end - replace_file_in_index file, new_hash - - if update - begin - patch_working_file file, new_hash - rescue => error - puts "Warning: failed updating #{file.src_path} in working copy: #{error}" - end - end - - true - end - - def format_object(file) - puts "Formatting #{file.src_path}" - - format_command = formatter.sub("{}", file.src_path.shellescape) - - pid1, r = pipe_command "git", "cat-file", "-p", file.dst_hash - pid2, r = pipe_command format_command, source: r - pid3, r = pipe_command "git", "hash-object", "-w", "--stdin", source: r - - result = r.readlines.map { |it| it.chomp } - if @verbose - result.each do |line| - puts "< #{line}" - end - end - - Process.wait pid1 - raise "Cannot read #{file.dst_hash} from object database" unless $?.success? - - Process.wait pid2 - raise "Error formatting #{file.src_path}" unless $?.success? - - Process.wait pid3 - raise "Error writing formatted file back to object database" unless $?.success? && !result.empty? - - result.first - end - - def object_is_empty(hash) - size = get_output("git", "cat-file", "-s", hash).first.to_i - size == 0 + def format_file(file) + new_hash = format_object file + + return true unless write + + if new_hash == file.dst_hash + puts "Unchanged #{file.src_path}" + return false end - def patch_working_file(file, new_hash) - patch = get_output "git", "diff", file.dst_hash, new_hash, lines: false - patch.gsub! "a/#{file.dst_hash}", "a/#{file.src_path}" - patch.gsub! "b/#{new_hash}", "b/#{file.src_path}" - - input, patch_out = IO.pipe - pid, r = pipe_command "git", "apply", "-", source: input - - patch_out.write patch - patch_out.close - - Process.wait pid - raise "Error applying patch" unless $?.success? + if object_is_empty new_hash + puts "Skipping #{file.src_path}, formatted file is empty" + return false end - def replace_file_in_index(file, new_hash) - get_output "git", "update-index", "--cacheinfo", "#{file.dst_mode},#{new_hash},#{file.src_path}" + replace_file_in_index file, new_hash + + if update + begin + patch_working_file file, new_hash + rescue StandardError => e + puts "Warning: failed updating #{file.src_path} in working copy: #{e}" + end end + + true + end + + def format_object(file) + puts "Formatting #{file.src_path}" + + format_command = formatter.sub('{}', file.src_path.shellescape) + + pid1, r = pipe_command 'git', 'cat-file', '-p', file.dst_hash + pid2, r = pipe_command format_command, source: r + pid3, r = pipe_command 'git', 'hash-object', '-w', '--stdin', source: r + + result = r.readlines.map { |it| it.chomp } + if @verbose + result.each do |line| + puts "< #{line}" + end + end + + Process.wait pid1 + raise "Cannot read #{file.dst_hash} from object database" unless $?.success? + + Process.wait pid2 + raise "Error formatting #{file.src_path}" unless $?.success? + + Process.wait pid3 + raise 'Error writing formatted file back to object database' unless $?.success? && !result.empty? + + result.first + end + + def object_is_empty(hash) + size = get_output('git', 'cat-file', '-s', hash).first.to_i + size == 0 + end + + def patch_working_file(file, new_hash) + patch = get_output 'git', 'diff', file.dst_hash, new_hash, lines: false + patch.gsub! "a/#{file.dst_hash}", "a/#{file.src_path}" + patch.gsub! "b/#{new_hash}", "b/#{file.src_path}" + + input, patch_out = IO.pipe + pid, r = pipe_command 'git', 'apply', '-', source: input + + patch_out.write patch + patch_out.close + + Process.wait pid + raise 'Error applying patch' unless $?.success? + end + + def replace_file_in_index(file, new_hash) + get_output 'git', 'update-index', '--cacheinfo', "#{file.dst_mode},#{new_hash},#{file.src_path}" + end end diff --git a/lib/format-staged/entry.rb b/lib/format-staged/entry.rb index 5f58847..66d4f23 100644 --- a/lib/format-staged/entry.rb +++ b/lib/format-staged/entry.rb @@ -1,9 +1,9 @@ -class FormatStaged +class FormatStaged class Entry PATTERN = /^:(?\d+) (?\d+) (?[a-f0-9]+) (?[a-f0-9]+) (?[A-Z])(?\d+)?\t(?[^\t]+)(?:\t(?[^\t]+))?$/ attr_reader :src_mode, :dst_mode, :src_hash, :dst_hash, :status, :score, :src_path, :dst_path, :path, :root - + def initialize(line, root:) matches = line.match(PATTERN) or raise "Cannot parse output #{line}" @src_mode = matches[:src_mode] @@ -17,17 +17,15 @@ class FormatStaged @path = File.expand_path(@src_path, root) @root = root end - + def symlink? @dst_mode == '120000' end - + def matches?(patterns) result = false patterns.each do |pattern| - if File.fnmatch? pattern, path, File::FNM_EXTGLOB - result = true - end + result = true if File.fnmatch? pattern, path, File::FNM_EXTGLOB end result end diff --git a/lib/format-staged/io.rb b/lib/format-staged/io.rb index 44a447b..9be09d1 100644 --- a/lib/format-staged/io.rb +++ b/lib/format-staged/io.rb @@ -1,7 +1,7 @@ -class FormatStaged +class FormatStaged def get_output(*args, lines: true) puts '> ' + args.join(' ') if @verbose - + output = IO.popen(args, err: :err) do |io| if lines io.readlines.map { |l| l.chomp } @@ -9,32 +9,32 @@ class FormatStaged io.read end end - + if @verbose and lines output.each do |line| puts "< #{line}" end end - - raise "Failed to run command" unless $?.success? - + + raise 'Failed to run command' unless $?.success? + output end - + def pipe_command(*args, source: nil) - puts (source.nil? ? '> ' : '| ') + args.join(' ') if @verbose + puts (source.nil? ? '> ' : '| ') + args.join(' ') if @verbose r, w = IO.pipe - + opts = {} opts[:in] = source unless source.nil? opts[:out] = w opts[:err] = :err - + pid = spawn(*args, **opts) - + w.close - source &.close - - return [pid, r] - end + source&.close + + [pid, r] + end end diff --git a/lib/format-staged/version.rb b/lib/format-staged/version.rb index dc206b1..a695e22 100644 --- a/lib/format-staged/version.rb +++ b/lib/format-staged/version.rb @@ -1,3 +1,3 @@ class FormatStaged - VERSION = "0.0.1" -end \ No newline at end of file + VERSION = '0.0.1' +end From 83b6b8f61a8bbe68f0de3375eedadb20680764db Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 19:25:35 +0200 Subject: [PATCH 04/72] rubocop -A --- Gemfile | 2 ++ bin/git-format-staged | 4 +++- format-staged.gemspec | 3 +++ lib/format-staged.rb | 17 ++++++++++------- lib/format-staged/entry.rb | 4 +++- lib/format-staged/io.rb | 11 +++++++---- lib/format-staged/version.rb | 2 ++ 7 files changed, 30 insertions(+), 13 deletions(-) diff --git a/Gemfile b/Gemfile index d65e2a6..f7200f1 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'http://rubygems.org' gemspec diff --git a/bin/git-format-staged b/bin/git-format-staged index a6772e5..d1ac2e7 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -1,4 +1,6 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + require 'format-staged' require 'optparse' @@ -46,7 +48,7 @@ end parser.parse! parameters[:patterns] = ARGV -if !parameters[:formatter] or parameters[:patterns].empty? +if !parameters[:formatter] || parameters[:patterns].empty? puts 'Missing formatter or file patterns!' puts parser diff --git a/format-staged.gemspec b/format-staged.gemspec index e69faed..ecdc19d 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -1,3 +1,5 @@ +# frozen_string_literal: true + lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'format-staged/version' @@ -13,6 +15,7 @@ Gem::Specification.new do |s| s.executables << 'git-format-staged' s.homepage = 'https://github.com/5sw/format-staged' s.license = 'MIT' + s.required_ruby_version = '2.7' s.add_development_dependency 'rubocop', '~> 1.29' end diff --git a/lib/format-staged.rb b/lib/format-staged.rb index 5e18a7f..d44f5aa 100644 --- a/lib/format-staged.rb +++ b/lib/format-staged.rb @@ -1,3 +1,6 @@ +# frozen_string_literal: true + +require 'English' require 'format-staged/version' require 'format-staged/entry' require 'format-staged/io' @@ -19,7 +22,7 @@ class FormatStaged files = get_output('git', 'diff-index', '--cached', '--diff-filter=AM', '--no-renames', 'HEAD') .map { |line| Entry.new(line, root: root) } - .reject { |entry| entry.symlink? } + .reject(&:symlink?) .filter { |entry| entry.matches?(@patterns) } files.each do |file| @@ -64,7 +67,7 @@ class FormatStaged pid2, r = pipe_command format_command, source: r pid3, r = pipe_command 'git', 'hash-object', '-w', '--stdin', source: r - result = r.readlines.map { |it| it.chomp } + result = r.readlines.map(&:chomp) if @verbose result.each do |line| puts "< #{line}" @@ -72,20 +75,20 @@ class FormatStaged end Process.wait pid1 - raise "Cannot read #{file.dst_hash} from object database" unless $?.success? + raise "Cannot read #{file.dst_hash} from object database" unless $CHILD_STATUS.success? Process.wait pid2 - raise "Error formatting #{file.src_path}" unless $?.success? + raise "Error formatting #{file.src_path}" unless $CHILD_STATUS.success? Process.wait pid3 - raise 'Error writing formatted file back to object database' unless $?.success? && !result.empty? + raise 'Error writing formatted file back to object database' unless $CHILD_STATUS.success? && !result.empty? result.first end def object_is_empty(hash) size = get_output('git', 'cat-file', '-s', hash).first.to_i - size == 0 + size.zero? end def patch_working_file(file, new_hash) @@ -100,7 +103,7 @@ class FormatStaged patch_out.close Process.wait pid - raise 'Error applying patch' unless $?.success? + raise 'Error applying patch' unless $CHILD_STATUS.success? end def replace_file_in_index(file, new_hash) diff --git a/lib/format-staged/entry.rb b/lib/format-staged/entry.rb index 66d4f23..82c171f 100644 --- a/lib/format-staged/entry.rb +++ b/lib/format-staged/entry.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + class FormatStaged class Entry - PATTERN = /^:(?\d+) (?\d+) (?[a-f0-9]+) (?[a-f0-9]+) (?[A-Z])(?\d+)?\t(?[^\t]+)(?:\t(?[^\t]+))?$/ + PATTERN = /^:(?\d+) (?\d+) (?[a-f0-9]+) (?[a-f0-9]+) (?[A-Z])(?\d+)?\t(?[^\t]+)(?:\t(?[^\t]+))?$/.freeze attr_reader :src_mode, :dst_mode, :src_hash, :dst_hash, :status, :score, :src_path, :dst_path, :path, :root diff --git a/lib/format-staged/io.rb b/lib/format-staged/io.rb index 9be09d1..5a80acd 100644 --- a/lib/format-staged/io.rb +++ b/lib/format-staged/io.rb @@ -1,22 +1,25 @@ +# frozen_string_literal: true + +require 'English' class FormatStaged def get_output(*args, lines: true) - puts '> ' + args.join(' ') if @verbose + puts "> #{args.join(' ')}" if @verbose output = IO.popen(args, err: :err) do |io| if lines - io.readlines.map { |l| l.chomp } + io.readlines.map(&:chomp) else io.read end end - if @verbose and lines + if @verbose && lines output.each do |line| puts "< #{line}" end end - raise 'Failed to run command' unless $?.success? + raise 'Failed to run command' unless $CHILD_STATUS.success? output end diff --git a/lib/format-staged/version.rb b/lib/format-staged/version.rb index a695e22..eb9c0cb 100644 --- a/lib/format-staged/version.rb +++ b/lib/format-staged/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class FormatStaged VERSION = '0.0.1' end From 47a691a8e9488ad2de2a5935fb5a7ca5ddd87552 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 19:54:22 +0200 Subject: [PATCH 05/72] More lint stuff --- .rubocop.yml | 5 +++++ bin/git-format-staged | 22 ++++++++++++++++++---- format-staged.gemspec | 4 ++++ lib/format-staged/entry.rb | 10 +++++++++- lib/{format-staged.rb => format_staged.rb} | 0 5 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 .rubocop.yml rename lib/{format-staged.rb => format_staged.rb} (100%) diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..888e1b6 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,5 @@ +AllCops: + NewCops: enable + TargetRubyVersion: 2.7 + Exclude: + - "vendor/**/*" diff --git a/bin/git-format-staged b/bin/git-format-staged index d1ac2e7..6e20263 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require 'format-staged' +require 'format_staged' require 'optparse' parameters = { @@ -14,17 +14,31 @@ parser = OptionParser.new do |opt| opt.banner = "Usage: #{opt.program_name} [options] [patterns]" opt.separator '' opt.on('-f', '--formatter COMMAND', - 'Shell command to format files, will run once per file. Occurrences of the placeholder `{}` will be replaced with a path to the file being formatted. (Example: "prettier --stdin-filepath \'{}\'")') do |o| + <<~DOC + Shell command to format files, will run once per file. Occurrences of the placeholder `{}` will \ + be replaced with a path to the file being formatted. \ + (Example: "prettier --stdin-filepath \'{}\'") + DOC + ) do |o| parameters[:formatter] = o end opt.on('--[no-]update-working-tree', - 'By default formatting changes made to staged file content will also be applied to working tree files via a patch. This option disables that behavior, leaving working tree files untouched.') do |value| + <<~DOC + By default formatting changes made to staged file content will also be applied to working tree \ + files via a patch. This option disables that behavior, leaving working tree files untouched.#{' '} + DOC + ) do |value| parameters[:update] = value end opt.on('--[no-]write', - "Prevents #{opt.program_name} from modifying staged or working tree files. You can use this option to check staged changes with a linter instead of formatting. With this option stdout from the formatter command is ignored.") do |value| + <<~DOC + "Prevents #{opt.program_name} from modifying staged or working tree files. You can use this option \ + to check staged changes with a linter instead of formatting. With this option stdout from the \ + formatter command is ignored." + DOC + ) do |value| parameters[:write] = value end diff --git a/format-staged.gemspec b/format-staged.gemspec index ecdc19d..ff70c99 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -18,4 +18,8 @@ Gem::Specification.new do |s| s.required_ruby_version = '2.7' s.add_development_dependency 'rubocop', '~> 1.29' + + s.metadata = { + 'rubygems_mfa_required' => 'true' + } end diff --git a/lib/format-staged/entry.rb b/lib/format-staged/entry.rb index 82c171f..3684b7d 100644 --- a/lib/format-staged/entry.rb +++ b/lib/format-staged/entry.rb @@ -2,7 +2,15 @@ class FormatStaged class Entry - PATTERN = /^:(?\d+) (?\d+) (?[a-f0-9]+) (?[a-f0-9]+) (?[A-Z])(?\d+)?\t(?[^\t]+)(?:\t(?[^\t]+))?$/.freeze + PATTERN = /^ + :(?\d+)\s + (?\d+)\s + (?[a-f0-9]+)\s + (?[a-f0-9]+)\s + (?[A-Z])(?\d+)?\t + (?[^\t]+) + (?:\t(?[^\t]+))? + $/x.freeze attr_reader :src_mode, :dst_mode, :src_hash, :dst_hash, :status, :score, :src_path, :dst_path, :path, :root diff --git a/lib/format-staged.rb b/lib/format_staged.rb similarity index 100% rename from lib/format-staged.rb rename to lib/format_staged.rb From 58e1e2402ccc1812303415e69cb72c50f48feede Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 20:21:33 +0200 Subject: [PATCH 06/72] Add Rakefile --- .gitignore | 1 + Gemfile.lock | 5 +++++ Rakefile | 16 ++++++++++++++++ format-staged.gemspec | 4 +++- 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 Rakefile diff --git a/.gitignore b/.gitignore index 7b5c283..9ec7ed6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ vendor/ .bundle/ +pkg/ diff --git a/Gemfile.lock b/Gemfile.lock index c450ff2..73d17ab 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,6 +11,7 @@ GEM parser (3.1.2.0) ast (~> 2.4.1) rainbow (3.1.1) + rake (13.0.6) regexp_parser (2.4.0) rexml (3.2.5) rubocop (1.29.1) @@ -24,6 +25,8 @@ GEM unicode-display_width (>= 1.4.0, < 3.0) rubocop-ast (1.18.0) parser (>= 3.1.1.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) ruby-progressbar (1.11.0) unicode-display_width (2.1.0) @@ -32,7 +35,9 @@ PLATFORMS DEPENDENCIES format-staged! + rake (~> 13.0) rubocop (~> 1.29) + rubocop-rake (~> 0.6) BUNDLED WITH 2.1.4 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..24bf06e --- /dev/null +++ b/Rakefile @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'bundler/gem_tasks' + +begin + Bundler.setup(:default, :development) +rescue Bundler::BundlerError => e + warn e.message + warn 'Run `bundle install` to install missing gems' + exit e.status_code +end + +require 'rubocop/rake_task' + +desc 'Run RuboCop' +RuboCop::RakeTask.new(:lint) diff --git a/format-staged.gemspec b/format-staged.gemspec index ff70c99..bf7935a 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -15,9 +15,11 @@ Gem::Specification.new do |s| s.executables << 'git-format-staged' s.homepage = 'https://github.com/5sw/format-staged' s.license = 'MIT' - s.required_ruby_version = '2.7' + s.required_ruby_version = '~> 2.7' + s.add_development_dependency 'rake', '~> 13.0' s.add_development_dependency 'rubocop', '~> 1.29' + s.add_development_dependency 'rubocop-rake', '~> 0.6' s.metadata = { 'rubygems_mfa_required' => 'true' From b5c1f4b55f5ca2f3f82f92067bb285d8ff7e7946 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 20:21:56 +0200 Subject: [PATCH 07/72] Bump version --- Gemfile.lock | 2 +- lib/format-staged/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 73d17ab..bc27cc5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - format-staged (0.0.1) + format-staged (0.0.2) GEM remote: http://rubygems.org/ diff --git a/lib/format-staged/version.rb b/lib/format-staged/version.rb index eb9c0cb..a0e42c2 100644 --- a/lib/format-staged/version.rb +++ b/lib/format-staged/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class FormatStaged - VERSION = '0.0.1' + VERSION = '0.0.2' end From 721ded5e42fcfbf2403302333e91c6b41d1c9a77 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 20:38:40 +0200 Subject: [PATCH 08/72] Add example pre-commit hook --- examples/pre-commit.rubocop | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 examples/pre-commit.rubocop diff --git a/examples/pre-commit.rubocop b/examples/pre-commit.rubocop new file mode 100755 index 0000000..c5ffa7a --- /dev/null +++ b/examples/pre-commit.rubocop @@ -0,0 +1,2 @@ +#!/bin/sh +bundle exec git-format-staged -f "bundle exec rubocop -x -s {} --stderr" "*.rb" --verbose \ No newline at end of file From 19bea3495cf72f7f196642bd881673abf29f31e9 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 21:02:05 +0200 Subject: [PATCH 09/72] Read output from git patch --- lib/format-staged/io.rb | 30 ++++++++++++++++-------------- lib/format_staged.rb | 11 ++++------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/lib/format-staged/io.rb b/lib/format-staged/io.rb index 5a80acd..6e99768 100644 --- a/lib/format-staged/io.rb +++ b/lib/format-staged/io.rb @@ -2,22 +2,11 @@ require 'English' class FormatStaged - def get_output(*args, lines: true) + def get_output(*args, lines: true, silent: false) puts "> #{args.join(' ')}" if @verbose - output = IO.popen(args, err: :err) do |io| - if lines - io.readlines.map(&:chomp) - else - io.read - end - end - - if @verbose && lines - output.each do |line| - puts "< #{line}" - end - end + r = IO.popen(args, err: :err) + output = read_output(r, lines: lines, silent: silent) raise 'Failed to run command' unless $CHILD_STATUS.success? @@ -40,4 +29,17 @@ class FormatStaged [pid, r] end + + def read_output(r, lines: true, silent: false) + result = r.read + splits = result.split("\n") + if @verbose && !silent + splits.each do |line| + puts "< #{line}" + end + end + r.close + + lines ? splits : result + end end diff --git a/lib/format_staged.rb b/lib/format_staged.rb index d44f5aa..df5c058 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -67,12 +67,7 @@ class FormatStaged pid2, r = pipe_command format_command, source: r pid3, r = pipe_command 'git', 'hash-object', '-w', '--stdin', source: r - result = r.readlines.map(&:chomp) - if @verbose - result.each do |line| - puts "< #{line}" - end - end + result = read_output(r, lines: false).chomp Process.wait pid1 raise "Cannot read #{file.dst_hash} from object database" unless $CHILD_STATUS.success? @@ -83,7 +78,7 @@ class FormatStaged Process.wait pid3 raise 'Error writing formatted file back to object database' unless $CHILD_STATUS.success? && !result.empty? - result.first + result end def object_is_empty(hash) @@ -102,6 +97,8 @@ class FormatStaged patch_out.write patch patch_out.close + read_output r + Process.wait pid raise 'Error applying patch' unless $CHILD_STATUS.success? end From d594c5239ef31b0f721c495c6093b0cb7792d8e5 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 21:03:26 +0200 Subject: [PATCH 10/72] =?UTF-8?q?Don=E2=80=99t=20dump=20patch=20in=20verbo?= =?UTF-8?q?se=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/format_staged.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index df5c058..2201c21 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -87,7 +87,7 @@ class FormatStaged end def patch_working_file(file, new_hash) - patch = get_output 'git', 'diff', file.dst_hash, new_hash, lines: false + patch = get_output 'git', 'diff', file.dst_hash, new_hash, lines: false, silent: true patch.gsub! "a/#{file.dst_hash}", "a/#{file.src_path}" patch.gsub! "b/#{new_hash}", "b/#{file.src_path}" From ccd69238c75259d2a2d882f0951d6ca397217245 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 21:10:08 +0200 Subject: [PATCH 11/72] Add .ruby-version --- .ruby-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .ruby-version diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..a603bb5 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.7.5 From 19703bfaa168b2c5ceade85cd96fcc393ec929ae Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 21:10:50 +0200 Subject: [PATCH 12/72] Bump version --- Gemfile.lock | 2 +- lib/format-staged/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index bc27cc5..049ae52 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - format-staged (0.0.2) + format-staged (0.0.3) GEM remote: http://rubygems.org/ diff --git a/lib/format-staged/version.rb b/lib/format-staged/version.rb index a0e42c2..ad2b122 100644 --- a/lib/format-staged/version.rb +++ b/lib/format-staged/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class FormatStaged - VERSION = '0.0.2' + VERSION = '0.0.3' end From c534bb04f6fbd27c9966de5ce3a85663ba0071d0 Mon Sep 17 00:00:00 2001 From: Sven Date: Wed, 25 May 2022 21:32:21 +0200 Subject: [PATCH 13/72] Create GitHub action to run rubocop --- .github/workflows/rubocop.yml | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .github/workflows/rubocop.yml diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml new file mode 100644 index 0000000..6142a00 --- /dev/null +++ b/.github/workflows/rubocop.yml @@ -0,0 +1,52 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# pulled from repo +name: "Rubocop" + +on: + push: + branches: [ main ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + schedule: + - cron: '20 4 * * 5' + +jobs: + rubocop: + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # If running on a self-hosted runner, check it meets the requirements + # listed at https://github.com/ruby/setup-ruby#using-self-hosted-runners + - name: Set up Ruby + uses: ruby/setup-ruby@f20f1eae726df008313d2e0d78c5e602562a1bcf + with: + ruby-version: 2.7 + + # This step is not necessary if you add the gem to your Gemfile + - name: Install Code Scanning integration + run: bundle add code-scanning-rubocop --skip-install + + - name: Install dependencies + run: bundle install + + - name: Rubocop run + run: | + bash -c " + bundle exec rubocop --require code_scanning --format CodeScanning::SarifFormatter -o rubocop.sarif + [[ $? -ne 2 ]] + " + + - name: Upload Sarif output + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: rubocop.sarif From d2a41716b235d36326f334d5e35db6eaaa3471e0 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 25 May 2022 22:02:48 +0200 Subject: [PATCH 14/72] Add rdoc comments to classes. --- lib/format-staged/entry.rb | 4 ++++ lib/format_staged.rb | 2 ++ 2 files changed, 6 insertions(+) diff --git a/lib/format-staged/entry.rb b/lib/format-staged/entry.rb index 3684b7d..4a5962d 100644 --- a/lib/format-staged/entry.rb +++ b/lib/format-staged/entry.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class FormatStaged + ## + # Entry in the git index. + # + # Data as produced by `git diff-index` class Entry PATTERN = /^ :(?\d+)\s diff --git a/lib/format_staged.rb b/lib/format_staged.rb index 2201c21..bac0ab4 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -6,6 +6,8 @@ require 'format-staged/entry' require 'format-staged/io' require 'shellwords' +## +# Runs staged changes through a formatting tool class FormatStaged attr_reader :formatter, :patterns, :update, :write, :verbose From bb0a3a80c446bdadc46778c2db8e5dc38a5d8f1e Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Thu, 26 May 2022 09:16:14 +0200 Subject: [PATCH 15/72] Add colorize gem + cli option --- Gemfile.lock | 2 ++ bin/git-format-staged | 4 ++++ format-staged.gemspec | 2 ++ lib/format-staged/io.rb | 1 + lib/format_staged.rb | 5 ++++- 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 049ae52..23520a5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,11 +2,13 @@ PATH remote: . specs: format-staged (0.0.3) + colorize GEM remote: http://rubygems.org/ specs: ast (2.4.2) + colorize (0.8.1) parallel (1.22.1) parser (3.1.2.0) ast (~> 2.4.1) diff --git a/bin/git-format-staged b/bin/git-format-staged index 6e20263..faa78af 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -57,6 +57,10 @@ parser = OptionParser.new do |opt| puts FormatStaged::VERSION exit end + + opt.on('--[no-]color', 'Colorizes output') do |value| + parameters[:color_output] = value + end end parser.parse! diff --git a/format-staged.gemspec b/format-staged.gemspec index bf7935a..ac7abdc 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -16,6 +16,8 @@ Gem::Specification.new do |s| s.homepage = 'https://github.com/5sw/format-staged' s.license = 'MIT' s.required_ruby_version = '~> 2.7' + + s.add_dependency 'colorize' s.add_development_dependency 'rake', '~> 13.0' s.add_development_dependency 'rubocop', '~> 1.29' diff --git a/lib/format-staged/io.rb b/lib/format-staged/io.rb index 6e99768..df472e3 100644 --- a/lib/format-staged/io.rb +++ b/lib/format-staged/io.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'English' + class FormatStaged def get_output(*args, lines: true, silent: false) puts "> #{args.join(' ')}" if @verbose diff --git a/lib/format_staged.rb b/lib/format_staged.rb index bac0ab4..d651d1c 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -5,18 +5,21 @@ require 'format-staged/version' require 'format-staged/entry' require 'format-staged/io' require 'shellwords' +require 'colorize' ## # Runs staged changes through a formatting tool class FormatStaged attr_reader :formatter, :patterns, :update, :write, :verbose - def initialize(formatter:, patterns:, update: true, write: true, verbose: true) + def initialize(formatter:, patterns:, update: true, write: true, verbose: true, color_output: nil) @formatter = formatter @patterns = patterns @update = update @write = write @verbose = verbose + + String.disable_colorization = !(color_output || STDOUT.isatty) end def run From eb0aa2105ce17c81b470a2c0862106ba0159c002 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Thu, 26 May 2022 09:29:09 +0200 Subject: [PATCH 16/72] Lint --- lib/format_staged.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index d651d1c..0eb0d9c 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -19,7 +19,7 @@ class FormatStaged @write = write @verbose = verbose - String.disable_colorization = !(color_output || STDOUT.isatty) + String.disable_colorization = !(color_output || $stdout.isatty) end def run From b8b7cc8d12a2ba567ca46afe1bd3dd040eeec8a4 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Thu, 26 May 2022 09:32:49 +0200 Subject: [PATCH 17/72] Cleanup --- bin/git-format-staged | 2 +- format-staged.gemspec | 2 +- lib/format-staged/io.rb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/git-format-staged b/bin/git-format-staged index faa78af..f325ac3 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -57,7 +57,7 @@ parser = OptionParser.new do |opt| puts FormatStaged::VERSION exit end - + opt.on('--[no-]color', 'Colorizes output') do |value| parameters[:color_output] = value end diff --git a/format-staged.gemspec b/format-staged.gemspec index ac7abdc..6711751 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -16,7 +16,7 @@ Gem::Specification.new do |s| s.homepage = 'https://github.com/5sw/format-staged' s.license = 'MIT' s.required_ruby_version = '~> 2.7' - + s.add_dependency 'colorize' s.add_development_dependency 'rake', '~> 13.0' diff --git a/lib/format-staged/io.rb b/lib/format-staged/io.rb index df472e3..c7cdaf5 100644 --- a/lib/format-staged/io.rb +++ b/lib/format-staged/io.rb @@ -31,15 +31,15 @@ class FormatStaged [pid, r] end - def read_output(r, lines: true, silent: false) - result = r.read + def read_output(output, lines: true, silent: false) + result = output.read splits = result.split("\n") if @verbose && !silent splits.each do |line| puts "< #{line}" end end - r.close + output.close lines ? splits : result end From 4fea7d525e890c78a7bbdac7008fb6c976dfb6da Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 09:25:09 +0200 Subject: [PATCH 18/72] Colour output --- lib/format-staged/io.rb | 16 ++++++++++++++++ lib/format_staged.rb | 11 +++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/lib/format-staged/io.rb b/lib/format-staged/io.rb index c7cdaf5..04b633b 100644 --- a/lib/format-staged/io.rb +++ b/lib/format-staged/io.rb @@ -43,4 +43,20 @@ class FormatStaged lines ? splits : result end + + def fail!(message) + abort "💣 #{message.red}" + end + + def warning(message) + warn "⚠️ #{message.yellow}" + end + + def info(message) + puts message.blue + end + + def verbose_info(message) + puts "ℹ️ #{message}" if verbose + end end diff --git a/lib/format_staged.rb b/lib/format_staged.rb index 0eb0d9c..804dbf3 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -23,8 +23,11 @@ class FormatStaged end def run + verbose_info "Finding repository root" root = get_output('git', 'rev-parse', '--show-toplevel').first + verbose_info "Repo at #{root}" + verbose_info "Listing staged files" files = get_output('git', 'diff-index', '--cached', '--diff-filter=AM', '--no-renames', 'HEAD') .map { |line| Entry.new(line, root: root) } .reject(&:symlink?) @@ -41,12 +44,12 @@ class FormatStaged return true unless write if new_hash == file.dst_hash - puts "Unchanged #{file.src_path}" + info "Unchanged #{file.src_path}" return false end if object_is_empty new_hash - puts "Skipping #{file.src_path}, formatted file is empty" + info "Skipping #{file.src_path}, formatted file is empty" return false end @@ -56,7 +59,7 @@ class FormatStaged begin patch_working_file file, new_hash rescue StandardError => e - puts "Warning: failed updating #{file.src_path} in working copy: #{e}" + warning "failed updating #{file.src_path} in working copy: #{e}" end end @@ -64,7 +67,7 @@ class FormatStaged end def format_object(file) - puts "Formatting #{file.src_path}" + info "Formatting #{file.src_path}" format_command = formatter.sub('{}', file.src_path.shellescape) From 77bab64a1249aaa87d724cc5076f3080f7a0be25 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 09:28:16 +0200 Subject: [PATCH 19/72] Message for updating the working copy --- lib/format_staged.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index 804dbf3..4bbcd86 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -95,6 +95,8 @@ class FormatStaged end def patch_working_file(file, new_hash) + info "Updating working copy" + patch = get_output 'git', 'diff', file.dst_hash, new_hash, lines: false, silent: true patch.gsub! "a/#{file.dst_hash}", "a/#{file.src_path}" patch.gsub! "b/#{new_hash}", "b/#{file.src_path}" From 2b75e3b6086392be6c2618aed43a160e135907fc Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 09:29:08 +0200 Subject: [PATCH 20/72] =?UTF-8?q?Don=E2=80=99t=20split=20lines=20where=20n?= =?UTF-8?q?ot=20needed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/format_staged.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index 4bbcd86..0cae333 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -24,7 +24,7 @@ class FormatStaged def run verbose_info "Finding repository root" - root = get_output('git', 'rev-parse', '--show-toplevel').first + root = get_output('git', 'rev-parse', '--show-toplevel', lines: false).chomp verbose_info "Repo at #{root}" verbose_info "Listing staged files" From f41d9dbb391cba328faab7af5df57b64938e76a5 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 09:40:39 +0200 Subject: [PATCH 21/72] Initialize using options ahsh --- lib/format_staged.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index 0cae333..8aa2160 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -12,14 +12,14 @@ require 'colorize' class FormatStaged attr_reader :formatter, :patterns, :update, :write, :verbose - def initialize(formatter:, patterns:, update: true, write: true, verbose: true, color_output: nil) + def initialize(formatter:, patterns:, **options) @formatter = formatter @patterns = patterns - @update = update - @write = write - @verbose = verbose + @update = options.fetch(:update, true) + @write = options.fetch(:write, true) + @verbose = options.fetch(:verbose, true) - String.disable_colorization = !(color_output || $stdout.isatty) + String.disable_colorization = !options.fetch(:color_output, $stdout.isatty) end def run From d933f690d48fa880588cbe240f5279b308a9e877 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 09:41:43 +0200 Subject: [PATCH 22/72] Split run into smaller methods --- lib/format_staged.rb | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index 8aa2160..a98964b 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -23,19 +23,26 @@ class FormatStaged end def run - verbose_info "Finding repository root" + matching_files(repo_root).each do |file| + format_file(file) + end + end + + def repo_root + verbose_info 'Finding repository root' root = get_output('git', 'rev-parse', '--show-toplevel', lines: false).chomp verbose_info "Repo at #{root}" - verbose_info "Listing staged files" - files = get_output('git', 'diff-index', '--cached', '--diff-filter=AM', '--no-renames', 'HEAD') - .map { |line| Entry.new(line, root: root) } - .reject(&:symlink?) - .filter { |entry| entry.matches?(@patterns) } + root + end - files.each do |file| - format_file(file) - end + def matching_files(root) + verbose_info 'Listing staged files' + + get_output('git', 'diff-index', '--cached', '--diff-filter=AM', '--no-renames', 'HEAD') + .map { |line| Entry.new(line, root: root) } + .reject(&:symlink?) + .filter { |entry| entry.matches?(@patterns) } end def format_file(file) From 5f8af000eec1e7cd8543e84f8093b4a62701e285 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 09:45:13 +0200 Subject: [PATCH 23/72] More refactoring --- lib/format_staged.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index a98964b..7cb9759 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -61,18 +61,21 @@ class FormatStaged end replace_file_in_index file, new_hash - - if update - begin - patch_working_file file, new_hash - rescue StandardError => e - warning "failed updating #{file.src_path} in working copy: #{e}" - end - end + update_working_copy file, new_hash true end + def update_working_copy(file, new_hash) + return unless update + + begin + patch_working_file file, new_hash + rescue StandardError => e + warning "failed updating #{file.src_path} in working copy: #{e}" + end + end + def format_object(file) info "Formatting #{file.src_path}" From 98302d88ea9e3243220e3e3ce7516036d3eb5fce Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 09:54:43 +0200 Subject: [PATCH 24/72] Single quotes --- lib/format_staged.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index 7cb9759..45d1bcf 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -105,7 +105,7 @@ class FormatStaged end def patch_working_file(file, new_hash) - info "Updating working copy" + info 'Updating working copy' patch = get_output 'git', 'diff', file.dst_hash, new_hash, lines: false, silent: true patch.gsub! "a/#{file.dst_hash}", "a/#{file.src_path}" From 664cb4d690d28f5f27c3f1ae9b52a463d5e796d0 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 09:54:52 +0200 Subject: [PATCH 25/72] Rubocop config --- .rubocop.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 888e1b6..e55c5d1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,3 +3,11 @@ AllCops: TargetRubyVersion: 2.7 Exclude: - "vendor/**/*" + +Metrics/MethodLength: + Max: 15 + +Metrics/BlockLength: + Exclude: + - bin/git-format-staged # Long block for option parser is ok + \ No newline at end of file From 2b26a3aa0e90786c25a0dcfc04c6457a6f3ebe52 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 10:05:04 +0200 Subject: [PATCH 26/72] Add code-scanning-rubocop to Gemfile --- .github/workflows/rubocop.yml | 6 +----- Gemfile | 4 ++++ Gemfile.lock | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 6142a00..4466c89 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -32,12 +32,8 @@ jobs: with: ruby-version: 2.7 - # This step is not necessary if you add the gem to your Gemfile - - name: Install Code Scanning integration - run: bundle add code-scanning-rubocop --skip-install - - name: Install dependencies - run: bundle install + run: bundle install --with=ci - name: Rubocop run run: | diff --git a/Gemfile b/Gemfile index f7200f1..c1f0eb2 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,7 @@ source 'http://rubygems.org' gemspec + +group :ci do + gem 'code-scanning-rubocop' +end diff --git a/Gemfile.lock b/Gemfile.lock index 23520a5..630522e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,6 +8,8 @@ GEM remote: http://rubygems.org/ specs: ast (2.4.2) + code-scanning-rubocop (0.6.1) + rubocop (~> 1.0) colorize (0.8.1) parallel (1.22.1) parser (3.1.2.0) @@ -36,6 +38,7 @@ PLATFORMS ruby DEPENDENCIES + code-scanning-rubocop format-staged! rake (~> 13.0) rubocop (~> 1.29) From 948ab8604b62f8855d97c8d0c13f89e76bbef953 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 10:20:33 +0200 Subject: [PATCH 27/72] Run rubocop via Rakefile --- .github/workflows/rubocop.yml | 4 +--- Rakefile | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 4466c89..d442459 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -25,8 +25,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - # If running on a self-hosted runner, check it meets the requirements - # listed at https://github.com/ruby/setup-ruby#using-self-hosted-runners - name: Set up Ruby uses: ruby/setup-ruby@f20f1eae726df008313d2e0d78c5e602562a1bcf with: @@ -38,7 +36,7 @@ jobs: - name: Rubocop run run: | bash -c " - bundle exec rubocop --require code_scanning --format CodeScanning::SarifFormatter -o rubocop.sarif + bundle exec rake lint_github [[ $? -ne 2 ]] " diff --git a/Rakefile b/Rakefile index 24bf06e..5f6bced 100644 --- a/Rakefile +++ b/Rakefile @@ -14,3 +14,10 @@ require 'rubocop/rake_task' desc 'Run RuboCop' RuboCop::RakeTask.new(:lint) + +desc 'Run RuboCop for GitHub' +RuboCop::RakeTask.new(:lint_github) do |t| + t.requires << 'code_scanning' + t.formatters << 'CodeScanning::SarifFormatter' + t.options << '-o' << 'rubocop.sarif' +end From 9339382f59bf9bdb0abbf764b63cdfe844ee88c2 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 10:23:34 +0200 Subject: [PATCH 28/72] Formatting --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index c1f0eb2..3cf16ce 100644 --- a/Gemfile +++ b/Gemfile @@ -5,5 +5,5 @@ source 'http://rubygems.org' gemspec group :ci do - gem 'code-scanning-rubocop' + gem 'code-scanning-rubocop' end From 6fd8bb9ce78afe111979dd814d6e49dc1c7cd670 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 10:34:50 +0200 Subject: [PATCH 29/72] Error out if negative pattern is specified --- lib/format_staged.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index 45d1bcf..dea8717 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -13,6 +13,8 @@ class FormatStaged attr_reader :formatter, :patterns, :update, :write, :verbose def initialize(formatter:, patterns:, **options) + validate_patterns patterns + @formatter = formatter @patterns = patterns @update = options.fetch(:update, true) @@ -126,4 +128,10 @@ class FormatStaged def replace_file_in_index(file, new_hash) get_output 'git', 'update-index', '--cacheinfo', "#{file.dst_mode},#{new_hash},#{file.src_path}" end + + def validate_patterns(patterns) + patterns.each do |pattern| + fail! "Negative pattern '#{pattern}' is not yet supported" if pattern.start_with? '!' + end + end end From 9fd78ac2e138ffb7e9b20eec93df8e6d63b9352b Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 10:47:07 +0200 Subject: [PATCH 30/72] Create top-level module --- bin/git-format-staged | 3 +- lib/format-staged/entry.rb | 2 +- lib/format-staged/io.rb | 98 ++++++++++++------------ lib/format-staged/job.rb | 141 +++++++++++++++++++++++++++++++++++ lib/format-staged/version.rb | 2 +- lib/format_staged.rb | 136 ++------------------------------- 6 files changed, 200 insertions(+), 182 deletions(-) create mode 100644 lib/format-staged/job.rb diff --git a/bin/git-format-staged b/bin/git-format-staged index f325ac3..a9454e3 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -73,5 +73,4 @@ if !parameters[:formatter] || parameters[:patterns].empty? exit end -formatter = FormatStaged.new(**parameters) -formatter.run +FormatStaged.run(**parameters) diff --git a/lib/format-staged/entry.rb b/lib/format-staged/entry.rb index 4a5962d..3828903 100644 --- a/lib/format-staged/entry.rb +++ b/lib/format-staged/entry.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class FormatStaged +module FormatStaged ## # Entry in the git index. # diff --git a/lib/format-staged/io.rb b/lib/format-staged/io.rb index 04b633b..10fd158 100644 --- a/lib/format-staged/io.rb +++ b/lib/format-staged/io.rb @@ -2,61 +2,65 @@ require 'English' -class FormatStaged - def get_output(*args, lines: true, silent: false) - puts "> #{args.join(' ')}" if @verbose +module FormatStaged + ## + # Mixin that provides IO methods + module IOMixin + def get_output(*args, lines: true, silent: false) + puts "> #{args.join(' ')}" if @verbose - r = IO.popen(args, err: :err) - output = read_output(r, lines: lines, silent: silent) + r = IO.popen(args, err: :err) + output = read_output(r, lines: lines, silent: silent) - raise 'Failed to run command' unless $CHILD_STATUS.success? + raise 'Failed to run command' unless $CHILD_STATUS.success? - output - end - - def pipe_command(*args, source: nil) - puts (source.nil? ? '> ' : '| ') + args.join(' ') if @verbose - r, w = IO.pipe - - opts = {} - opts[:in] = source unless source.nil? - opts[:out] = w - opts[:err] = :err - - pid = spawn(*args, **opts) - - w.close - source&.close - - [pid, r] - end - - def read_output(output, lines: true, silent: false) - result = output.read - splits = result.split("\n") - if @verbose && !silent - splits.each do |line| - puts "< #{line}" - end + output end - output.close - lines ? splits : result - end + def pipe_command(*args, source: nil) + puts (source.nil? ? '> ' : '| ') + args.join(' ') if @verbose + r, w = IO.pipe - def fail!(message) - abort "💣 #{message.red}" - end + opts = {} + opts[:in] = source unless source.nil? + opts[:out] = w + opts[:err] = :err - def warning(message) - warn "⚠️ #{message.yellow}" - end + pid = spawn(*args, **opts) - def info(message) - puts message.blue - end + w.close + source&.close - def verbose_info(message) - puts "ℹ️ #{message}" if verbose + [pid, r] + end + + def read_output(output, lines: true, silent: false) + result = output.read + splits = result.split("\n") + if @verbose && !silent + splits.each do |line| + puts "< #{line}" + end + end + output.close + + lines ? splits : result + end + + def fail!(message) + abort "💣 #{message.red}" + end + + def warning(message) + warn "⚠️ #{message.yellow}" + end + + def info(message) + puts message.blue + end + + def verbose_info(message) + puts "ℹ️ #{message}" if verbose + end end end diff --git a/lib/format-staged/job.rb b/lib/format-staged/job.rb new file mode 100644 index 0000000..4955e3a --- /dev/null +++ b/lib/format-staged/job.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +require_relative 'entry' +require_relative 'io' + +require 'shellwords' +require 'colorize' +require 'English' + +module FormatStaged + ## + # Runs staged changes through a formatting tool + class Job + include IOMixin + + attr_reader :formatter, :patterns, :update, :write, :verbose + + def initialize(formatter:, patterns:, **options) + validate_patterns patterns + + @formatter = formatter + @patterns = patterns + @update = options.fetch(:update, true) + @write = options.fetch(:write, true) + @verbose = options.fetch(:verbose, true) + + String.disable_colorization = !options.fetch(:color_output, $stdout.isatty) + end + + def run + matching_files(repo_root).each do |file| + format_file(file) + end + end + + def repo_root + verbose_info 'Finding repository root' + root = get_output('git', 'rev-parse', '--show-toplevel', lines: false).chomp + verbose_info "Repo at #{root}" + + root + end + + def matching_files(root) + verbose_info 'Listing staged files' + + get_output('git', 'diff-index', '--cached', '--diff-filter=AM', '--no-renames', 'HEAD') + .map { |line| Entry.new(line, root: root) } + .reject(&:symlink?) + .filter { |entry| entry.matches?(@patterns) } + end + + def format_file(file) + new_hash = format_object file + + return true unless write + + if new_hash == file.dst_hash + info "Unchanged #{file.src_path}" + return false + end + + if object_is_empty new_hash + info "Skipping #{file.src_path}, formatted file is empty" + return false + end + + replace_file_in_index file, new_hash + update_working_copy file, new_hash + + true + end + + def update_working_copy(file, new_hash) + return unless update + + begin + patch_working_file file, new_hash + rescue StandardError => e + warning "failed updating #{file.src_path} in working copy: #{e}" + end + end + + def format_object(file) + info "Formatting #{file.src_path}" + + format_command = formatter.sub('{}', file.src_path.shellescape) + + pid1, r = pipe_command 'git', 'cat-file', '-p', file.dst_hash + pid2, r = pipe_command format_command, source: r + pid3, r = pipe_command 'git', 'hash-object', '-w', '--stdin', source: r + + result = read_output(r, lines: false).chomp + + Process.wait pid1 + raise "Cannot read #{file.dst_hash} from object database" unless $CHILD_STATUS.success? + + Process.wait pid2 + raise "Error formatting #{file.src_path}" unless $CHILD_STATUS.success? + + Process.wait pid3 + raise 'Error writing formatted file back to object database' unless $CHILD_STATUS.success? && !result.empty? + + result + end + + def object_is_empty(hash) + size = get_output('git', 'cat-file', '-s', hash).first.to_i + size.zero? + end + + def patch_working_file(file, new_hash) + info 'Updating working copy' + + patch = get_output 'git', 'diff', file.dst_hash, new_hash, lines: false, silent: true + patch.gsub! "a/#{file.dst_hash}", "a/#{file.src_path}" + patch.gsub! "b/#{new_hash}", "b/#{file.src_path}" + + input, patch_out = IO.pipe + pid, r = pipe_command 'git', 'apply', '-', source: input + + patch_out.write patch + patch_out.close + + read_output r + + Process.wait pid + raise 'Error applying patch' unless $CHILD_STATUS.success? + end + + def replace_file_in_index(file, new_hash) + get_output 'git', 'update-index', '--cacheinfo', "#{file.dst_mode},#{new_hash},#{file.src_path}" + end + + def validate_patterns(patterns) + patterns.each do |pattern| + fail! "Negative pattern '#{pattern}' is not yet supported" if pattern.start_with? '!' + end + end + end +end diff --git a/lib/format-staged/version.rb b/lib/format-staged/version.rb index ad2b122..bcc8b1a 100644 --- a/lib/format-staged/version.rb +++ b/lib/format-staged/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -class FormatStaged +module FormatStaged VERSION = '0.0.3' end diff --git a/lib/format_staged.rb b/lib/format_staged.rb index dea8717..d6e8bf2 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -1,137 +1,11 @@ # frozen_string_literal: true -require 'English' -require 'format-staged/version' -require 'format-staged/entry' -require 'format-staged/io' -require 'shellwords' -require 'colorize' +require_relative 'format-staged/job' ## -# Runs staged changes through a formatting tool -class FormatStaged - attr_reader :formatter, :patterns, :update, :write, :verbose - - def initialize(formatter:, patterns:, **options) - validate_patterns patterns - - @formatter = formatter - @patterns = patterns - @update = options.fetch(:update, true) - @write = options.fetch(:write, true) - @verbose = options.fetch(:verbose, true) - - String.disable_colorization = !options.fetch(:color_output, $stdout.isatty) - end - - def run - matching_files(repo_root).each do |file| - format_file(file) - end - end - - def repo_root - verbose_info 'Finding repository root' - root = get_output('git', 'rev-parse', '--show-toplevel', lines: false).chomp - verbose_info "Repo at #{root}" - - root - end - - def matching_files(root) - verbose_info 'Listing staged files' - - get_output('git', 'diff-index', '--cached', '--diff-filter=AM', '--no-renames', 'HEAD') - .map { |line| Entry.new(line, root: root) } - .reject(&:symlink?) - .filter { |entry| entry.matches?(@patterns) } - end - - def format_file(file) - new_hash = format_object file - - return true unless write - - if new_hash == file.dst_hash - info "Unchanged #{file.src_path}" - return false - end - - if object_is_empty new_hash - info "Skipping #{file.src_path}, formatted file is empty" - return false - end - - replace_file_in_index file, new_hash - update_working_copy file, new_hash - - true - end - - def update_working_copy(file, new_hash) - return unless update - - begin - patch_working_file file, new_hash - rescue StandardError => e - warning "failed updating #{file.src_path} in working copy: #{e}" - end - end - - def format_object(file) - info "Formatting #{file.src_path}" - - format_command = formatter.sub('{}', file.src_path.shellescape) - - pid1, r = pipe_command 'git', 'cat-file', '-p', file.dst_hash - pid2, r = pipe_command format_command, source: r - pid3, r = pipe_command 'git', 'hash-object', '-w', '--stdin', source: r - - result = read_output(r, lines: false).chomp - - Process.wait pid1 - raise "Cannot read #{file.dst_hash} from object database" unless $CHILD_STATUS.success? - - Process.wait pid2 - raise "Error formatting #{file.src_path}" unless $CHILD_STATUS.success? - - Process.wait pid3 - raise 'Error writing formatted file back to object database' unless $CHILD_STATUS.success? && !result.empty? - - result - end - - def object_is_empty(hash) - size = get_output('git', 'cat-file', '-s', hash).first.to_i - size.zero? - end - - def patch_working_file(file, new_hash) - info 'Updating working copy' - - patch = get_output 'git', 'diff', file.dst_hash, new_hash, lines: false, silent: true - patch.gsub! "a/#{file.dst_hash}", "a/#{file.src_path}" - patch.gsub! "b/#{new_hash}", "b/#{file.src_path}" - - input, patch_out = IO.pipe - pid, r = pipe_command 'git', 'apply', '-', source: input - - patch_out.write patch - patch_out.close - - read_output r - - Process.wait pid - raise 'Error applying patch' unless $CHILD_STATUS.success? - end - - def replace_file_in_index(file, new_hash) - get_output 'git', 'update-index', '--cacheinfo', "#{file.dst_mode},#{new_hash},#{file.src_path}" - end - - def validate_patterns(patterns) - patterns.each do |pattern| - fail! "Negative pattern '#{pattern}' is not yet supported" if pattern.start_with? '!' - end +# FormatStaged module +module FormatStaged + def self.run(**options) + Job.new(**options).run end end From 29f580b4312736334f2a63e01d72e15019ab2bba Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 11:14:54 +0200 Subject: [PATCH 31/72] Prepare for rspec tests --- Gemfile.lock | 15 +++++++++++++++ Rakefile | 3 +++ format-staged.gemspec | 1 + 3 files changed, 19 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 630522e..1576cac 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,6 +11,7 @@ GEM code-scanning-rubocop (0.6.1) rubocop (~> 1.0) colorize (0.8.1) + diff-lcs (1.5.0) parallel (1.22.1) parser (3.1.2.0) ast (~> 2.4.1) @@ -18,6 +19,19 @@ GEM rake (13.0.6) regexp_parser (2.4.0) rexml (3.2.5) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-support (3.11.0) rubocop (1.29.1) parallel (~> 1.10) parser (>= 3.1.0.0) @@ -41,6 +55,7 @@ DEPENDENCIES code-scanning-rubocop format-staged! rake (~> 13.0) + rspec rubocop (~> 1.29) rubocop-rake (~> 0.6) diff --git a/Rakefile b/Rakefile index 5f6bced..78e0d48 100644 --- a/Rakefile +++ b/Rakefile @@ -21,3 +21,6 @@ RuboCop::RakeTask.new(:lint_github) do |t| t.formatters << 'CodeScanning::SarifFormatter' t.options << '-o' << 'rubocop.sarif' end + +require 'rspec/core/rake_task' +RSpec::Core::RakeTask.new(:spec) diff --git a/format-staged.gemspec b/format-staged.gemspec index 6711751..e48cb9e 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rake', '~> 13.0' s.add_development_dependency 'rubocop', '~> 1.29' s.add_development_dependency 'rubocop-rake', '~> 0.6' + s.add_development_dependency 'rspec' s.metadata = { 'rubygems_mfa_required' => 'true' From d52e6ea10e1239cfe3f9e568f2a75f07154cab52 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 12:17:28 +0200 Subject: [PATCH 32/72] Prepare for testing --- Gemfile.lock | 3 ++ format-staged.gemspec | 3 +- spec/git.rb | 72 +++++++++++++++++++++++++++++++++++++++++++ spec/test_spec.rb | 22 +++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 spec/git.rb create mode 100644 spec/test_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index 1576cac..f7fd5a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -45,6 +45,8 @@ GEM parser (>= 3.1.1.0) rubocop-rake (0.6.0) rubocop (~> 1.0) + rubocop-rspec (2.11.1) + rubocop (~> 1.19) ruby-progressbar (1.11.0) unicode-display_width (2.1.0) @@ -58,6 +60,7 @@ DEPENDENCIES rspec rubocop (~> 1.29) rubocop-rake (~> 0.6) + rubocop-rspec BUNDLED WITH 2.1.4 diff --git a/format-staged.gemspec b/format-staged.gemspec index e48cb9e..8640d53 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -20,9 +20,10 @@ Gem::Specification.new do |s| s.add_dependency 'colorize' s.add_development_dependency 'rake', '~> 13.0' + s.add_development_dependency 'rspec' s.add_development_dependency 'rubocop', '~> 1.29' s.add_development_dependency 'rubocop-rake', '~> 0.6' - s.add_development_dependency 'rspec' + s.add_development_dependency 'rubocop-rspec' s.metadata = { 'rubygems_mfa_required' => 'true' diff --git a/spec/git.rb b/spec/git.rb new file mode 100644 index 0000000..6d5ec56 --- /dev/null +++ b/spec/git.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'tmpdir' +require 'fileutils' +require 'English' + +## +# Test helpers for managing a git repository +module Git + ## + # A git repository + class Repo + attr_reader :path + + def initialize + @path = Dir.mktmpdir + + git 'init' + end + + def file_in_tree(name, content) + set_content name, content + stage name + commit "Add #{name}" + end + + def set_content(name, content) + absolute = Pathname.new(path) + name + + FileUtils.mkdir_p absolute.dirname + File.write absolute, content.end_with?("\n") ? content : "#{content}\n" + end + + def stage(file) + git 'add', file + end + + def commit(message) + git 'commit', '-m', message + end + + def content(file) + git 'show', ":#{file}" + end + + def cleanup + FileUtils.remove_entry path + end + + def git(*cmd) + in_repo do + output = IO.popen(['git'] + cmd) do |io| + io.read.chomp + end + + raise 'Failed to run git' unless $CHILD_STATUS.success? + + output + end + end + + def in_repo(&block) + Dir.chdir path, &block + end + end + + def self.new_repo + repo = Repo.new + yield repo if block_given? + repo + end +end diff --git a/spec/test_spec.rb b/spec/test_spec.rb new file mode 100644 index 0000000..778f761 --- /dev/null +++ b/spec/test_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require_relative 'git' + +require 'format_staged' + +describe FormatStaged do + def repo + @repo ||= Git.new_repo do |r| + r.file_in_tree 'index.js', <<~CONTENT + function foo () { + return 'foo' + } + CONTENT + end + end + + after :each do + @repo&.cleanup + @repo = nil + end +end From 736f394281f85756f43a3d9f05bb36b447e74223 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 29 May 2022 12:51:43 +0200 Subject: [PATCH 33/72] Set tab size --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ff30c44 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.tabSize": 2 +} \ No newline at end of file From 3711acbec3c0a00d6e74fb6e0232d1b2cdc67d59 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 5 Jun 2022 12:40:48 +0200 Subject: [PATCH 34/72] Test basic functionality --- .rubocop.yml | 2 +- spec/git.rb | 14 ++++++++++++++ spec/test_hook.rb | 6 ++++++ spec/test_spec.rb | 45 +++++++++++++++++++++++++++++++++++++++------ 4 files changed, 60 insertions(+), 7 deletions(-) create mode 100755 spec/test_hook.rb diff --git a/.rubocop.yml b/.rubocop.yml index e55c5d1..72f47ae 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -10,4 +10,4 @@ Metrics/MethodLength: Metrics/BlockLength: Exclude: - bin/git-format-staged # Long block for option parser is ok - \ No newline at end of file + - spec/**/*.rb diff --git a/spec/git.rb b/spec/git.rb index 6d5ec56..17cad1f 100644 --- a/spec/git.rb +++ b/spec/git.rb @@ -31,6 +31,14 @@ module Git File.write absolute, content.end_with?("\n") ? content : "#{content}\n" end + def get_content(name) + File.read(Pathname.new(path) + name).chomp + end + + def get_staged(name) + git 'show', ":#{name}" + end + def stage(file) git 'add', file end @@ -62,6 +70,12 @@ module Git def in_repo(&block) Dir.chdir path, &block end + + def run_formatter + in_repo do + FormatStaged.run formatter: "#{__dir__}/test_hook.rb {}", patterns: ['*.test'] + end + end end def self.new_repo diff --git a/spec/test_hook.rb b/spec/test_hook.rb new file mode 100755 index 0000000..0dd2109 --- /dev/null +++ b/spec/test_hook.rb @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +$stdin.readlines.each do |line| + puts line.gsub(/([^\s]*)\s*=\s*(.*)/, '\1 = \2') +end diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 778f761..2b3ef4c 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -6,12 +6,8 @@ require 'format_staged' describe FormatStaged do def repo - @repo ||= Git.new_repo do |r| - r.file_in_tree 'index.js', <<~CONTENT - function foo () { - return 'foo' - } - CONTENT + @repo ||= Git.new_repo do |repo| + repo.file_in_tree 'origin.test', 'x = y' end end @@ -19,4 +15,41 @@ describe FormatStaged do @repo&.cleanup @repo = nil end + + it 'updates staged file and working copy' do + repo.set_content 'test.test', 'a=b' + repo.stage 'test.test' + repo.run_formatter + + expect(repo.get_staged('test.test')).to eq('a = b') + expect(repo.get_content('test.test')).to eq('a = b') + end + + it 'leaves other changes in working copy' do + repo.set_content 'test.test', "x=y\na=b\n" + repo.stage 'test.test' + repo.set_content 'test.test', 'abc' + repo.run_formatter + + expect(repo.get_content('test.test')).to eq('abc') + expect(repo.get_staged('test.test')).to eq("x = y\na = b") + end + + it 'merges update to working copy' do + repo.set_content 'test.test', "x=y\n#stuff\n" + repo.stage 'test.test' + repo.set_content 'test.test', "x=y\n#stuff\nmore stuff\n" + repo.run_formatter + + expect(repo.get_content('test.test')).to eq("x = y\n#stuff\nmore stuff") + expect(repo.get_staged('test.test')).to eq("x = y\n#stuff") + end + + it 'only touches files matching the given pattern' do + repo.set_content 'test.other', 'x=y' + repo.stage 'test.other' + repo.run_formatter + + expect(repo.get_content('test.other')).to eq('x=y') + end end From 64d2eb9a432236184c856b1c3d13a5bc86e3b82f Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 5 Jun 2022 12:57:27 +0200 Subject: [PATCH 35/72] Add github action for rspec --- .github/workflows/rspec.yml | 34 ++++++++++++++++++++++++++++++++++ Gemfile | 1 + Rakefile | 4 ++++ 3 files changed, 39 insertions(+) create mode 100644 .github/workflows/rspec.yml diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml new file mode 100644 index 0000000..3538355 --- /dev/null +++ b/.github/workflows/rspec.yml @@ -0,0 +1,34 @@ +name: "RSpec" + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + rspec: + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@f20f1eae726df008313d2e0d78c5e602562a1bcf + with: + ruby-version: 2.7 + + - name: Install dependencies + run: bundle install --with=ci + + - name: Run tests + run: bundle exec rake spec_github + + - name: Publish Test Report + uses: mikepenz/action-junit-report@v3 + if: always() # always run even if the previous step fails + with: + report_paths: 'rspec.xml' diff --git a/Gemfile b/Gemfile index 3cf16ce..b37c960 100644 --- a/Gemfile +++ b/Gemfile @@ -6,4 +6,5 @@ gemspec group :ci do gem 'code-scanning-rubocop' + gem "rspec_junit_formatter" end diff --git a/Rakefile b/Rakefile index 78e0d48..f0ec11c 100644 --- a/Rakefile +++ b/Rakefile @@ -24,3 +24,7 @@ end require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) + +RSpec::Core::RakeTask.new(:spec_github) do |t| + t.rspec_opts = '--format RspecJunitFormatter --out rspec.xml' +end From b1c6f0bff76a5deedb6566337b9b8e9e1bd2eb4d Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 5 Jun 2022 12:57:42 +0200 Subject: [PATCH 36/72] Ignore rspec.xml --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9ec7ed6..e54ca87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor/ .bundle/ pkg/ +rspec.xml From 6eab549d8aa6ab8092d8b3e45ae15865a0ab589f Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 5 Jun 2022 13:00:07 +0200 Subject: [PATCH 37/72] Fix yaml --- .github/workflows/rspec.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 3538355..e7b18a3 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -27,8 +27,8 @@ jobs: - name: Run tests run: bundle exec rake spec_github - - name: Publish Test Report - uses: mikepenz/action-junit-report@v3 - if: always() # always run even if the previous step fails - with: - report_paths: 'rspec.xml' + - name: Publish Test Report + uses: mikepenz/action-junit-report@v3 + if: always() # always run even if the previous step fails + with: + report_paths: 'rspec.xml' From c76200e4a6c715f94829bb0ed0db7c66f17a3b95 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 5 Jun 2022 13:03:51 +0200 Subject: [PATCH 38/72] Configure git --- spec/git.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/git.rb b/spec/git.rb index 17cad1f..474e7b5 100644 --- a/spec/git.rb +++ b/spec/git.rb @@ -16,6 +16,9 @@ module Git @path = Dir.mktmpdir git 'init' + git 'branch', '-m', 'main' + git 'config', 'user.name', 'Test User' + git 'config', 'user.email', 'test@example.com' end def file_in_tree(name, content) From b138a43b80e15a77f9d8d2de895616db3e44c951 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 5 Jun 2022 13:03:58 +0200 Subject: [PATCH 39/72] Gemfile.lock --- Gemfile.lock | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index f7fd5a9..01a5147 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -32,6 +32,8 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.11.0) rspec-support (3.11.0) + rspec_junit_formatter (0.5.1) + rspec-core (>= 2, < 4, != 2.12.0) rubocop (1.29.1) parallel (~> 1.10) parser (>= 3.1.0.0) @@ -58,6 +60,7 @@ DEPENDENCIES format-staged! rake (~> 13.0) rspec + rspec_junit_formatter rubocop (~> 1.29) rubocop-rake (~> 0.6) rubocop-rspec From d00760732dfe3d064ccff1683bc5d095961bf1a1 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 5 Jun 2022 13:19:08 +0200 Subject: [PATCH 40/72] Specify branch name with git init --- spec/git.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/git.rb b/spec/git.rb index 474e7b5..34128b1 100644 --- a/spec/git.rb +++ b/spec/git.rb @@ -15,8 +15,7 @@ module Git def initialize @path = Dir.mktmpdir - git 'init' - git 'branch', '-m', 'main' + git 'init', '-b', 'main' git 'config', 'user.name', 'Test User' git 'config', 'user.email', 'test@example.com' end From ba54c407cbad615f468dbb69112b3b536e9198ea Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 10:18:05 +0200 Subject: [PATCH 41/72] [#1] Fail if all files are reset to committed version. --- Gemfile | 2 +- bin/git-format-staged | 3 ++- lib/format-staged/job.rb | 17 ++++++++++++++--- spec/test_spec.rb | 27 +++++++++++++++++++++++---- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index b37c960..26ea26a 100644 --- a/Gemfile +++ b/Gemfile @@ -6,5 +6,5 @@ gemspec group :ci do gem 'code-scanning-rubocop' - gem "rspec_junit_formatter" + gem 'rspec_junit_formatter' end diff --git a/bin/git-format-staged b/bin/git-format-staged index a9454e3..5195cfa 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -73,4 +73,5 @@ if !parameters[:formatter] || parameters[:patterns].empty? exit end -FormatStaged.run(**parameters) +success = FormatStaged.run(**parameters) +exit success ? 0 : 1 diff --git a/lib/format-staged/job.rb b/lib/format-staged/job.rb index 4955e3a..6ad0b44 100644 --- a/lib/format-staged/job.rb +++ b/lib/format-staged/job.rb @@ -28,9 +28,15 @@ module FormatStaged end def run - matching_files(repo_root).each do |file| - format_file(file) + files = matching_files(repo_root) + if files.empty? + info 'No staged file matching pattern. Done' + return true end + + formatted = files.filter { |file| format_file file } + + !formatted.empty? end def repo_root @@ -57,7 +63,7 @@ module FormatStaged if new_hash == file.dst_hash info "Unchanged #{file.src_path}" - return false + return true end if object_is_empty new_hash @@ -68,6 +74,11 @@ module FormatStaged replace_file_in_index file, new_hash update_working_copy file, new_hash + if new_hash == file.src_hash + info "File #{file.src_path} equal to comitted" + return false + end + true end diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 2b3ef4c..1c30107 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -19,8 +19,9 @@ describe FormatStaged do it 'updates staged file and working copy' do repo.set_content 'test.test', 'a=b' repo.stage 'test.test' - repo.run_formatter + success = repo.run_formatter + expect(success).to be_truthy expect(repo.get_staged('test.test')).to eq('a = b') expect(repo.get_content('test.test')).to eq('a = b') end @@ -29,8 +30,9 @@ describe FormatStaged do repo.set_content 'test.test', "x=y\na=b\n" repo.stage 'test.test' repo.set_content 'test.test', 'abc' - repo.run_formatter + success = repo.run_formatter + expect(success).to be_truthy expect(repo.get_content('test.test')).to eq('abc') expect(repo.get_staged('test.test')).to eq("x = y\na = b") end @@ -39,8 +41,9 @@ describe FormatStaged do repo.set_content 'test.test', "x=y\n#stuff\n" repo.stage 'test.test' repo.set_content 'test.test', "x=y\n#stuff\nmore stuff\n" - repo.run_formatter + success = repo.run_formatter + expect(success).to be_truthy expect(repo.get_content('test.test')).to eq("x = y\n#stuff\nmore stuff") expect(repo.get_staged('test.test')).to eq("x = y\n#stuff") end @@ -48,8 +51,24 @@ describe FormatStaged do it 'only touches files matching the given pattern' do repo.set_content 'test.other', 'x=y' repo.stage 'test.other' - repo.run_formatter + success = repo.run_formatter + expect(success).to be_truthy expect(repo.get_content('test.other')).to eq('x=y') end + + it 'fails if files are changed to already comitted version' do + repo.file_in_tree 'test.test', 'x = y' + repo.set_content 'test.test', 'x=y' + repo.stage 'test.test' + + success = repo.run_formatter + + expect(success).to be_falsy + expect(repo.get_content('test.test')).to eq('x = y') + end + + it 'succeeds if there are no staged files' do + expect(repo.run_formatter).to be_truthy + end end From f4657ba967631cf6f3e0dd650b000923056f50e1 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 10:42:09 +0200 Subject: [PATCH 42/72] Expand test hook to return empty output if line `#clear` is in input + write spec for test hook. --- spec/test_hook.rb | 8 +++++++- spec/test_hook_spec.rb | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 spec/test_hook_spec.rb diff --git a/spec/test_hook.rb b/spec/test_hook.rb index 0dd2109..380c9c7 100755 --- a/spec/test_hook.rb +++ b/spec/test_hook.rb @@ -1,6 +1,12 @@ #!/usr/bin/env ruby # frozen_string_literal: true +output = [] $stdin.readlines.each do |line| - puts line.gsub(/([^\s]*)\s*=\s*(.*)/, '\1 = \2') + exit 0 if line.chomp == '#clear' + output << line.gsub(/([^\s]*)\s*=\s*(.*)/, '\1 = \2') +end + +output.each do |line| + puts line end diff --git a/spec/test_hook_spec.rb b/spec/test_hook_spec.rb new file mode 100644 index 0000000..167de49 --- /dev/null +++ b/spec/test_hook_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +describe 'Test Hook' do + it 'adds spaces around =' do + out = run 'a=b' + + expect(out).to eq('a = b') + end + + it 'returns empty output if it as a #clear line' do + out = run "a=b\n#clear\nc = d" + expect(out).to eq '' + end + + def run(input) + IO.popen ['ruby', "#{__dir__}/test_hook.rb"], mode: File::RDWR do |io| + io.write input + io.close_write + io.read.chomp + end + end +end From 47363ad14b4638b51026e0ea1f56d469e15cf5c9 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 10:58:40 +0200 Subject: [PATCH 43/72] Fail if test_hook cannot be run --- spec/test_hook_spec.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/test_hook_spec.rb b/spec/test_hook_spec.rb index 167de49..a17c07b 100644 --- a/spec/test_hook_spec.rb +++ b/spec/test_hook_spec.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'English' + describe 'Test Hook' do it 'adds spaces around =' do out = run 'a=b' @@ -13,10 +15,14 @@ describe 'Test Hook' do end def run(input) - IO.popen ['ruby', "#{__dir__}/test_hook.rb"], mode: File::RDWR do |io| + result = IO.popen ['ruby', "#{__dir__}/test_hook.rb"], mode: File::RDWR do |io| io.write input io.close_write io.read.chomp end + + raise 'Cannot run test_hook.rb' unless $CHILD_STATUS.success? + + result end end From 2e5906245444932351d24b6c7b856c5548ee991e Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:27:39 +0200 Subject: [PATCH 44/72] Use git to check for empty index. --- lib/format-staged/io.rb | 11 +++++++++ lib/format-staged/job.rb | 7 ++++-- spec/test_spec.rb | 50 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/lib/format-staged/io.rb b/lib/format-staged/io.rb index 10fd158..8d3bf5d 100644 --- a/lib/format-staged/io.rb +++ b/lib/format-staged/io.rb @@ -17,6 +17,17 @@ module FormatStaged output end + def get_status(*args) + puts "> #{args.join(' ')}" if @verbose + result = system(*args) + + raise 'Failed to run command' if result.nil? + + puts "? #{$CHILD_STATUS.exitstatus}" if @verbose + + $CHILD_STATUS.exitstatus + end + def pipe_command(*args, source: nil) puts (source.nil? ? '> ' : '| ') + args.join(' ') if @verbose r, w = IO.pipe diff --git a/lib/format-staged/job.rb b/lib/format-staged/job.rb index 6ad0b44..bc6acd9 100644 --- a/lib/format-staged/job.rb +++ b/lib/format-staged/job.rb @@ -36,7 +36,10 @@ module FormatStaged formatted = files.filter { |file| format_file file } - !formatted.empty? + return false unless formatted.size == files.size + + quiet = @verbose ? [] : ['--quiet'] + return get_status('git', 'diff-index', '--cached', '--exit-code', *quiet, 'HEAD') != 0 end def repo_root @@ -76,7 +79,7 @@ module FormatStaged if new_hash == file.src_hash info "File #{file.src_path} equal to comitted" - return false + return true end true diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 1c30107..54edcb2 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -57,7 +57,7 @@ describe FormatStaged do expect(repo.get_content('test.other')).to eq('x=y') end - it 'fails if files are changed to already comitted version' do + it 'fails if all files are changed to already comitted version' do repo.file_in_tree 'test.test', 'x = y' repo.set_content 'test.test', 'x=y' repo.stage 'test.test' @@ -68,7 +68,55 @@ describe FormatStaged do expect(repo.get_content('test.test')).to eq('x = y') end + it 'succeeds if there are excluded files to commit' do + repo.file_in_tree 'test.test', 'x = y' + repo.set_content 'test.test', 'x=y' + repo.stage 'test.test' + repo.set_content 'test.other', 'abc' + repo.stage 'test.other' + + success = repo.run_formatter + + expect(success).to be_truthy + expect(repo.get_content('test.test')).to eq('x = y') + end + it 'succeeds if there are no staged files' do expect(repo.run_formatter).to be_truthy end + + it 'succeeds if only excluded files are changed' do + repo.set_content 'test.other', 'abc' + repo.stage 'test.other' + + expect(repo.run_formatter).to be_truthy + end + + it 'succeeds if one file is changed' do + repo.file_in_tree 'test.test', 'x = y' + repo.set_content 'test.test', 'x=y' + repo.stage 'test.test' + repo.set_content 'other.test', 'a=b' + repo.stage 'other.test' + + success = repo.run_formatter + + expect(success).to be_truthy + expect(repo.get_content('test.test')).to eq('x = y') + expect(repo.get_content('other.test')).to eq('a = b') + end + + it 'fails if a single file becomes empty' do + repo.file_in_tree 'test.test', 'x = y' + repo.set_content 'test.test', '#clear' + repo.stage 'test.test' + repo.set_content 'other.test', 'a=b' + repo.stage 'other.test' + + success = repo.run_formatter + + expect(success).to be_falsy + expect(repo.get_content('test.test')).to eq('#clear') + expect(repo.get_content('other.test')).to eq('a = b') + end end From 8d8471951a96332209b4b89a1a2bb460c34bb2c1 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:30:55 +0200 Subject: [PATCH 45/72] Add fail mode to test_hook.rb --- spec/test_hook.rb | 1 + spec/test_hook_spec.rb | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/spec/test_hook.rb b/spec/test_hook.rb index 380c9c7..595050e 100755 --- a/spec/test_hook.rb +++ b/spec/test_hook.rb @@ -4,6 +4,7 @@ output = [] $stdin.readlines.each do |line| exit 0 if line.chomp == '#clear' + exit 1 if line.chomp == '#fail' output << line.gsub(/([^\s]*)\s*=\s*(.*)/, '\1 = \2') end diff --git a/spec/test_hook_spec.rb b/spec/test_hook_spec.rb index a17c07b..5d647a2 100644 --- a/spec/test_hook_spec.rb +++ b/spec/test_hook_spec.rb @@ -14,6 +14,10 @@ describe 'Test Hook' do expect(out).to eq '' end + it 'fails with a #fail line' do + expect { run '#fail' }.to raise_error + end + def run(input) result = IO.popen ['ruby', "#{__dir__}/test_hook.rb"], mode: File::RDWR do |io| io.write input From 159c0ac25b20d736dd0c6c03bb17bcb44986a0e0 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:32:59 +0200 Subject: [PATCH 46/72] Verify that a failing formatter fails the whole process --- spec/test_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 54edcb2..5ebb9e2 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -119,4 +119,10 @@ describe FormatStaged do expect(repo.get_content('test.test')).to eq('#clear') expect(repo.get_content('other.test')).to eq('a = b') end + + it 'fails if the hook returns a nonzero status' do + repo.set_content 'test.test', '#fail' + repo.stage 'test.test' + expect { repo.run_formatter }.to raise_error + end end From 9a1890249593ae14e8c125356688f536c7bb2e8a Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:34:19 +0200 Subject: [PATCH 47/72] Allow additional arguments to run_formatter --- spec/git.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/git.rb b/spec/git.rb index 34128b1..7ffa1e3 100644 --- a/spec/git.rb +++ b/spec/git.rb @@ -73,9 +73,9 @@ module Git Dir.chdir path, &block end - def run_formatter + def run_formatter(**arguments) in_repo do - FormatStaged.run formatter: "#{__dir__}/test_hook.rb {}", patterns: ['*.test'] + FormatStaged.run(**arguments, formatter: "#{__dir__}/test_hook.rb {}", patterns: ['*.test']) end end end From 7da4a0e0f015a9dfe0f6c236a3263956dc569782 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:38:37 +0200 Subject: [PATCH 48/72] Check that files stay unchanged in write mode. --- spec/test_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 5ebb9e2..6951a2a 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -125,4 +125,13 @@ describe FormatStaged do repo.stage 'test.test' expect { repo.run_formatter }.to raise_error end + + it 'leaves files alone when write is false' do + repo.set_content 'test.test', 'a=b' + repo.stage 'test.test' + + expect(repo.run_formatter write: false).to be_truthy + expect(repo.get_content('test.test')).to eq 'a=b' + expect(repo.get_staged('test.test')).to eq 'a=b' + end end From aad741f8cc9c580a4f4258f2a6296b2f30f69e4c Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:40:40 +0200 Subject: [PATCH 49/72] Only check for staged changes if writing output --- lib/format-staged/job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/format-staged/job.rb b/lib/format-staged/job.rb index bc6acd9..e061fec 100644 --- a/lib/format-staged/job.rb +++ b/lib/format-staged/job.rb @@ -39,7 +39,7 @@ module FormatStaged return false unless formatted.size == files.size quiet = @verbose ? [] : ['--quiet'] - return get_status('git', 'diff-index', '--cached', '--exit-code', *quiet, 'HEAD') != 0 + return !write || get_status('git', 'diff-index', '--cached', '--exit-code', *quiet, 'HEAD') != 0 end def repo_root From 95633bfd9fa5ca3f8cd73282e1a73127b72523d7 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:43:44 +0200 Subject: [PATCH 50/72] =?UTF-8?q?Don=E2=80=99t=20write=20formatter=20outpu?= =?UTF-8?q?t=20to=20object=20database=20if=20not=20updating=20the=20repo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/format-staged/job.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/format-staged/job.rb b/lib/format-staged/job.rb index e061fec..18bacc8 100644 --- a/lib/format-staged/job.rb +++ b/lib/format-staged/job.rb @@ -102,7 +102,9 @@ module FormatStaged pid1, r = pipe_command 'git', 'cat-file', '-p', file.dst_hash pid2, r = pipe_command format_command, source: r - pid3, r = pipe_command 'git', 'hash-object', '-w', '--stdin', source: r + + write_args = write ? ['-w'] : [] + pid3, r = pipe_command 'git', 'hash-object', *write_args, '--stdin', source: r result = read_output(r, lines: false).chomp From 3c7bfbc4378eec89cae1a7859cda531937ed2d05 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:50:57 +0200 Subject: [PATCH 51/72] Verify error messages --- spec/test_hook_spec.rb | 4 +++- spec/test_spec.rb | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/test_hook_spec.rb b/spec/test_hook_spec.rb index 5d647a2..236279d 100644 --- a/spec/test_hook_spec.rb +++ b/spec/test_hook_spec.rb @@ -15,7 +15,9 @@ describe 'Test Hook' do end it 'fails with a #fail line' do - expect { run '#fail' }.to raise_error + expect { run '#fail' }.to raise_error(RuntimeError) { |error| + expect(error.message).to eq('Cannot run test_hook.rb') + } end def run(input) diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 6951a2a..ce53719 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -123,7 +123,9 @@ describe FormatStaged do it 'fails if the hook returns a nonzero status' do repo.set_content 'test.test', '#fail' repo.stage 'test.test' - expect { repo.run_formatter }.to raise_error + expect { repo.run_formatter }.to raise_error(RuntimeError) { |error| + expect(error.message).to eq 'Error formatting test.test' + } end it 'leaves files alone when write is false' do From c738bc5eba76d463d0b0a198386db9f146c4f5ca Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:55:43 +0200 Subject: [PATCH 52/72] Print exception message and exit 1 --- bin/git-format-staged | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bin/git-format-staged b/bin/git-format-staged index 5195cfa..41d0829 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -73,5 +73,10 @@ if !parameters[:formatter] || parameters[:patterns].empty? exit end -success = FormatStaged.run(**parameters) -exit success ? 0 : 1 +begin + success = FormatStaged.run(**parameters) + exit success ? 0 : 1 +rescue RuntimeError => e + puts "💣 Error: #{e.message}".red + exit 1 +end From cd2dddcb3ead6a9b4fbefb7b478010b19255f96e Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Mon, 6 Jun 2022 11:56:07 +0200 Subject: [PATCH 53/72] Bump version --- Gemfile.lock | 2 +- lib/format-staged/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 01a5147..3ee6feb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - format-staged (0.0.3) + format-staged (0.1.0) colorize GEM diff --git a/lib/format-staged/version.rb b/lib/format-staged/version.rb index bcc8b1a..06bba8d 100644 --- a/lib/format-staged/version.rb +++ b/lib/format-staged/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module FormatStaged - VERSION = '0.0.3' + VERSION = '0.1.0' end From 7254cc338c7797020b1cfe93c62add07e0b4f852 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Tue, 7 Jun 2022 09:25:41 +0200 Subject: [PATCH 54/72] Allow ruby >= 2.7 --- format-staged.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format-staged.gemspec b/format-staged.gemspec index 8640d53..d8fd383 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |s| s.executables << 'git-format-staged' s.homepage = 'https://github.com/5sw/format-staged' s.license = 'MIT' - s.required_ruby_version = '~> 2.7' + s.required_ruby_version = '>= 2.7' s.add_dependency 'colorize' From 2dd04cac7b76ea395ad4b068f2d2be1edeebd3bc Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Tue, 7 Jun 2022 09:29:46 +0200 Subject: [PATCH 55/72] Run tests with ruby 2.7 and 3.0 --- .github/workflows/rspec.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index e7b18a3..d2b4d7e 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -11,6 +11,10 @@ jobs: runs-on: ubuntu-latest strategy: fail-fast: false + matrix: + ruby: + - 2.7 + - 3.0 steps: - name: Checkout repository @@ -19,7 +23,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@f20f1eae726df008313d2e0d78c5e602562a1bcf with: - ruby-version: 2.7 + ruby-version: ${{ matrix.ruby }} - name: Install dependencies run: bundle install --with=ci @@ -31,4 +35,6 @@ jobs: uses: mikepenz/action-junit-report@v3 if: always() # always run even if the previous step fails with: - report_paths: 'rspec.xml' + report_paths: rspec.xml + check_name: Rspec for Ruby ${{ matrix.ruby }} + fail_on_failure: true From 667931bfc20191c43654443ec237df0cae8ca25c Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Tue, 7 Jun 2022 09:31:58 +0200 Subject: [PATCH 56/72] Strings for ruby versions --- .github/workflows/rspec.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index d2b4d7e..40f4a67 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -13,8 +13,8 @@ jobs: fail-fast: false matrix: ruby: - - 2.7 - - 3.0 + - '2.7' + - '3.0' steps: - name: Checkout repository From 477cc57f801514a1eb603b0f7bf671a0d26cdaa6 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Tue, 7 Jun 2022 09:33:41 +0200 Subject: [PATCH 57/72] Pin commit for action-junit-report --- .github/workflows/rspec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 40f4a67..2ddcae6 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -32,7 +32,7 @@ jobs: run: bundle exec rake spec_github - name: Publish Test Report - uses: mikepenz/action-junit-report@v3 + uses: mikepenz/action-junit-report@41a3188dde10229782fd78cd72fc574884dd7686 if: always() # always run even if the previous step fails with: report_paths: rspec.xml From 496cb0832459fc5e24cce309de74bd57c87ee91e Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Tue, 7 Jun 2022 09:48:19 +0200 Subject: [PATCH 58/72] Bump version --- Gemfile.lock | 2 +- lib/format-staged/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3ee6feb..677e689 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - format-staged (0.1.0) + format-staged (0.1.1) colorize GEM diff --git a/lib/format-staged/version.rb b/lib/format-staged/version.rb index 06bba8d..ff59012 100644 --- a/lib/format-staged/version.rb +++ b/lib/format-staged/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module FormatStaged - VERSION = '0.1.0' + VERSION = '0.1.1' end From 32dc629db3afb5e3c09fe2e83de7c884ac00487b Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Tue, 7 Jun 2022 10:14:43 +0200 Subject: [PATCH 59/72] Update readme file. Content stolen from the original git-format-staged repo --- README.md | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bfa56d..c204339 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,96 @@ # git-format-staged -Port of [hallettj/git-format-staged](https://github.com/hallettj/git-format-staged) to Ruby. +Port of [hallettj/git-format-staged](https://github.com/hallettj/git-format-staged) +to Ruby. + +Consider a project where you want all code formatted consistently. So you use +a formatter and/or linter. (For example [SwiftFormat][]) You want to make sure +that everyone working on the project runs the formatter, add a git pre-commit +hook to run it. The naive way to write that hook would be to: + +- get a list of staged files +- run the formatter on those files +- run `git add` to stage the results of formatting + +The problem with that solution is it forces you to commit entire files. At +worst this will lead to contributors to unwittingly committing changes. At +best it disrupts workflow for contributors who use `git add -p`. + +git-format-staged tackles this problem by running the formatter on the staged +version of the file. Staging changes to a file actually produces a new file +that exists in the git object database. git-format-staged uses some git +plumbing commands to send content from that file to your formatter. The command +replaces file content in the git index. The process bypasses the working tree, +so any unstaged changes are ignored by the formatter, and remain unstaged. + +After formatting a staged file git-format-staged computes a patch which it +attempts to apply to the working tree file to keep the working tree in sync +with staged changes. If patching fails you will see a warning message. The +version of the file that is committed will be formatted properly - the warning +just means that working tree copy of the file has been left unformatted. The +patch step can be disabled with the `--no-update-working-tree` option. + +[SwiftFormat]: https://github.com/nicklockwood/SwiftFormat + +## How to install + +Requires Ruby 2.7 or newer. Tests run on 2.7 and 3.0. + +Install as a development dependency in a project that uses bundle to manage +Ruby dependencies: + + $ bundle add format-staged + +Or install globally: + + $ gem install format-staged + +## How to use + +For detailed information run: + + $ [bundle exec] git-format-staged --help + +The command expects a shell command to run a formatter, and one or more file +patterns to identify which files should be formatted. For example: + + $ git-format-staged --formatter 'prettier --stdin-filepath "{}"' '*.js' + +That will format all `.js` files using `prettier`. + +The formatter command must read file content from `stdin`, and output formatted +content to `stdout`. + +Patterns are evaluated from left-to-right: if a file matches multiple patterns +the right-most pattern determines whether the file is included or excluded. + +git-format-staged never operates on files that are excluded from version +control. So it is not necessary to explicitly exclude stuff like +`vendor/`. + +The formatter command may include a placeholder, `{}`, which will be replaced +with the path of the file that is being formatted. This is useful if your +formatter needs to know the file extension to determine how to format or to +lint each file. For example: + + $ git-format-staged -f 'prettier --stdin-filepath "{}"' '*.js' '*.css' + +Do not attempt to read or write to `{}` in your formatter command! The +placeholder exists only for referencing the file name and path. + +### Check staged changes with a linter without formatting + +Perhaps you do not want to reformat files automatically; but you do want to +prevent files from being committed if they do not conform to style rules. You +can use git-format-staged with the `--no-write` option, and supply a lint +command instead of a format command. Here is an example using ESLint: + + $ git-format-staged --no-write -f 'eslint --stdin --stdin-filename "{}" >&2' 'src/*.js' + +If this command is run in a pre-commit hook, and the lint command fails the +commit will be aborted and error messages will be displayed. The lint command +must read file content via `stdin`. Anything that the lint command outputs to +`stdout` will be ignored. In the example above `eslint` is given the `--stdin` +option to tell it to read content from `stdin` instead of reading files from +disk, and messages from `eslint` are redirected to `stderr` (using the `>&2` +notation) so that you can see them. From f5ce138392a94b169a51b2fc2230f29dc4cc1c92 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Tue, 7 Jun 2022 10:15:40 +0200 Subject: [PATCH 60/72] [#5] Load version file --- lib/format_staged.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/format_staged.rb b/lib/format_staged.rb index d6e8bf2..683244f 100644 --- a/lib/format_staged.rb +++ b/lib/format_staged.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative 'format-staged/job' +require_relative 'format-staged/version' ## # FormatStaged module From bfc0eb7e45dc5f74129878c0a5eaf1de90b4a99e Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Tue, 7 Jun 2022 10:16:36 +0200 Subject: [PATCH 61/72] Bump version --- Gemfile.lock | 2 +- lib/format-staged/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 677e689..9bd2244 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - format-staged (0.1.1) + format-staged (0.1.2) colorize GEM diff --git a/lib/format-staged/version.rb b/lib/format-staged/version.rb index ff59012..d9e035e 100644 --- a/lib/format-staged/version.rb +++ b/lib/format-staged/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module FormatStaged - VERSION = '0.1.1' + VERSION = '0.1.2' end From 6171a51720bfd8505a5b770c2f2badfabf3001a1 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Tue, 7 Jun 2022 10:34:12 +0200 Subject: [PATCH 62/72] Add explanation why Ruby --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index c204339..bcf923c 100644 --- a/README.md +++ b/README.md @@ -94,3 +94,16 @@ must read file content via `stdin`. Anything that the lint command outputs to option to tell it to read content from `stdin` instead of reading files from disk, and messages from `eslint` are redirected to `stderr` (using the `>&2` notation) so that you can see them. + +### Why the Ruby port if there already is a fine Python implementation? + +I don’t like Python ;) + +But jokes aside, I am already setting up a Ruby environment (using [rbenv][]) for my +projects to run [cocoapods][] and [fastlane][] and our git hooks. By using this port +we don’t need to ensure to have python available as well. + + +[rbenv]: https://github.com/rbenv/rbenv/ +[cocoapods]: https://cocoapods.org/ +[fastlane]: https://fastlane.tools/ From d092c3650fc53fbb8c3556822eaa3ee190693def Mon Sep 17 00:00:00 2001 From: Sven Date: Sun, 7 Aug 2022 11:18:22 +0200 Subject: [PATCH 63/72] Remove schedule from Rubocop workflow --- .github/workflows/rubocop.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index d442459..732cf2b 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -12,8 +12,6 @@ on: pull_request: # The branches below must be a subset of the branches above branches: [ main ] - schedule: - - cron: '20 4 * * 5' jobs: rubocop: From 672ad20150a3ba617866585026e96d3670cc6331 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 19:55:42 +0000 Subject: [PATCH 64/72] Bump rexml from 3.2.5 to 3.2.8 Bumps [rexml](https://github.com/ruby/rexml) from 3.2.5 to 3.2.8. - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.2.5...v3.2.8) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9bd2244..bfe6880 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,7 +18,8 @@ GEM rainbow (3.1.1) rake (13.0.6) regexp_parser (2.4.0) - rexml (3.2.5) + rexml (3.2.8) + strscan (>= 3.0.9) rspec (3.11.0) rspec-core (~> 3.11.0) rspec-expectations (~> 3.11.0) @@ -50,6 +51,7 @@ GEM rubocop-rspec (2.11.1) rubocop (~> 1.19) ruby-progressbar (1.11.0) + strscan (3.1.0) unicode-display_width (2.1.0) PLATFORMS From 40fe55854a4dd0d70cbe4952e512639a9863d940 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sun, 16 Jun 2024 17:51:45 +0200 Subject: [PATCH 65/72] Update dependencies --- Gemfile.lock | 66 +++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9bd2244..1877d4e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,47 +10,55 @@ GEM ast (2.4.2) code-scanning-rubocop (0.6.1) rubocop (~> 1.0) - colorize (0.8.1) - diff-lcs (1.5.0) - parallel (1.22.1) - parser (3.1.2.0) + colorize (1.1.0) + diff-lcs (1.5.1) + json (2.7.2) + language_server-protocol (3.17.0.3) + parallel (1.25.1) + parser (3.3.3.0) ast (~> 2.4.1) + racc + racc (1.8.0) rainbow (3.1.1) - rake (13.0.6) - regexp_parser (2.4.0) - rexml (3.2.5) - rspec (3.11.0) - rspec-core (~> 3.11.0) - rspec-expectations (~> 3.11.0) - rspec-mocks (~> 3.11.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rake (13.2.1) + regexp_parser (2.9.2) + rexml (3.3.0) + strscan + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-mocks (3.11.1) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-support (3.11.0) - rspec_junit_formatter (0.5.1) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) + rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.29.1) + rubocop (1.64.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.1.0.0) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.17.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.18.0) - parser (>= 3.1.1.0) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (2.11.1) - rubocop (~> 1.19) - ruby-progressbar (1.11.0) - unicode-display_width (2.1.0) + rubocop-rspec (3.0.1) + rubocop (~> 1.61) + ruby-progressbar (1.13.0) + strscan (3.1.0) + unicode-display_width (2.5.0) PLATFORMS ruby From 8fa719df7ae4fb4140a525f084954bd3344b9b04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 16:42:11 +0000 Subject: [PATCH 66/72] Bump rexml from 3.3.0 to 3.3.3 Bumps [rexml](https://github.com/ruby/rexml) from 3.3.0 to 3.3.3. - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.3.0...v3.3.3) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1877d4e..a9fa1ce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -22,7 +22,7 @@ GEM rainbow (3.1.1) rake (13.2.1) regexp_parser (2.9.2) - rexml (3.3.0) + rexml (3.3.3) strscan rspec (3.13.0) rspec-core (~> 3.13.0) From 120bb4b79adcbbc91aaf83b69bd70fca8f370b23 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sat, 3 Aug 2024 19:21:16 +0200 Subject: [PATCH 67/72] Update dependencies --- Gemfile.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a9fa1ce..f16d3e1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,14 +15,14 @@ GEM json (2.7.2) language_server-protocol (3.17.0.3) parallel (1.25.1) - parser (3.3.3.0) + parser (3.3.4.0) ast (~> 2.4.1) racc - racc (1.8.0) + racc (1.8.1) rainbow (3.1.1) rake (13.2.1) regexp_parser (2.9.2) - rexml (3.3.3) + rexml (3.3.4) strscan rspec (3.13.0) rspec-core (~> 3.13.0) @@ -39,13 +39,13 @@ GEM rspec-support (3.13.1) rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.64.1) + rubocop (1.65.1) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) + regexp_parser (>= 2.4, < 3.0) rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) @@ -54,7 +54,7 @@ GEM parser (>= 3.3.1.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (3.0.1) + rubocop-rspec (3.0.3) rubocop (~> 1.61) ruby-progressbar (1.13.0) strscan (3.1.0) From 604fe35be60d16c9a97535eb0b62b54be58d3a77 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sat, 3 Aug 2024 19:23:03 +0200 Subject: [PATCH 68/72] autocorrect --- bin/git-format-staged | 2 +- lib/format-staged/job.rb | 2 +- spec/test_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/git-format-staged b/bin/git-format-staged index 41d0829..2111bcc 100755 --- a/bin/git-format-staged +++ b/bin/git-format-staged @@ -17,7 +17,7 @@ parser = OptionParser.new do |opt| <<~DOC Shell command to format files, will run once per file. Occurrences of the placeholder `{}` will \ be replaced with a path to the file being formatted. \ - (Example: "prettier --stdin-filepath \'{}\'") + (Example: "prettier --stdin-filepath '{}'") DOC ) do |o| parameters[:formatter] = o diff --git a/lib/format-staged/job.rb b/lib/format-staged/job.rb index 18bacc8..176b099 100644 --- a/lib/format-staged/job.rb +++ b/lib/format-staged/job.rb @@ -39,7 +39,7 @@ module FormatStaged return false unless formatted.size == files.size quiet = @verbose ? [] : ['--quiet'] - return !write || get_status('git', 'diff-index', '--cached', '--exit-code', *quiet, 'HEAD') != 0 + !write || get_status('git', 'diff-index', '--cached', '--exit-code', *quiet, 'HEAD') != 0 end def repo_root diff --git a/spec/test_spec.rb b/spec/test_spec.rb index ce53719..26fc0ff 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -132,7 +132,7 @@ describe FormatStaged do repo.set_content 'test.test', 'a=b' repo.stage 'test.test' - expect(repo.run_formatter write: false).to be_truthy + expect(repo.run_formatter(write: false)).to be_truthy expect(repo.get_content('test.test')).to eq 'a=b' expect(repo.get_staged('test.test')).to eq 'a=b' end From 567198c1827dce911f5c98ab9753061f2c7895ad Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sat, 3 Aug 2024 19:28:01 +0200 Subject: [PATCH 69/72] add dev gems to Gemfile instead of gemspec --- Gemfile | 7 +++++++ format-staged.gemspec | 6 ------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 26ea26a..7cfcf2a 100644 --- a/Gemfile +++ b/Gemfile @@ -8,3 +8,10 @@ group :ci do gem 'code-scanning-rubocop' gem 'rspec_junit_formatter' end + + +gem 'rake', '~> 13.0' +gem 'rspec' +gem 'rubocop', '~> 1.29' +gem 'rubocop-rake', '~> 0.6' +gem 'rubocop-rspec' diff --git a/format-staged.gemspec b/format-staged.gemspec index d8fd383..ddde6c8 100644 --- a/format-staged.gemspec +++ b/format-staged.gemspec @@ -19,12 +19,6 @@ Gem::Specification.new do |s| s.add_dependency 'colorize' - s.add_development_dependency 'rake', '~> 13.0' - s.add_development_dependency 'rspec' - s.add_development_dependency 'rubocop', '~> 1.29' - s.add_development_dependency 'rubocop-rake', '~> 0.6' - s.add_development_dependency 'rubocop-rspec' - s.metadata = { 'rubygems_mfa_required' => 'true' } From e894f65f304aafe515994d3b4e998c3e25f071c1 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Sat, 3 Aug 2024 19:43:09 +0200 Subject: [PATCH 70/72] Remove rubocop-rspec for now --- .rubocop.yml | 3 +++ Gemfile | 1 - Gemfile.lock | 3 --- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 72f47ae..f3d5561 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,6 @@ +require: + - rubocop-rake + AllCops: NewCops: enable TargetRubyVersion: 2.7 diff --git a/Gemfile b/Gemfile index 7cfcf2a..c57d2a7 100644 --- a/Gemfile +++ b/Gemfile @@ -14,4 +14,3 @@ gem 'rake', '~> 13.0' gem 'rspec' gem 'rubocop', '~> 1.29' gem 'rubocop-rake', '~> 0.6' -gem 'rubocop-rspec' diff --git a/Gemfile.lock b/Gemfile.lock index f16d3e1..0280dfa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -54,8 +54,6 @@ GEM parser (>= 3.3.1.0) rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (3.0.3) - rubocop (~> 1.61) ruby-progressbar (1.13.0) strscan (3.1.0) unicode-display_width (2.5.0) @@ -71,7 +69,6 @@ DEPENDENCIES rspec_junit_formatter rubocop (~> 1.29) rubocop-rake (~> 0.6) - rubocop-rspec BUNDLED WITH 2.1.4 From 0af937edac5c913e2d89ab104ab606ccb1f90ce5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 20:55:01 +0000 Subject: [PATCH 71/72] Bump rexml from 3.3.4 to 3.3.6 Bumps [rexml](https://github.com/ruby/rexml) from 3.3.4 to 3.3.6. - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.3.4...v3.3.6) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0280dfa..d145a96 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -22,7 +22,7 @@ GEM rainbow (3.1.1) rake (13.2.1) regexp_parser (2.9.2) - rexml (3.3.4) + rexml (3.3.6) strscan rspec (3.13.0) rspec-core (~> 3.13.0) From 04c243009688068d46228aaaf34483688adf20a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:52:26 +0000 Subject: [PATCH 72/72] Bump rexml from 3.3.6 to 3.3.9 Bumps [rexml](https://github.com/ruby/rexml) from 3.3.6 to 3.3.9. - [Release notes](https://github.com/ruby/rexml/releases) - [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md) - [Commits](https://github.com/ruby/rexml/compare/v3.3.6...v3.3.9) --- updated-dependencies: - dependency-name: rexml dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d145a96..9344382 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -22,8 +22,7 @@ GEM rainbow (3.1.1) rake (13.2.1) regexp_parser (2.9.2) - rexml (3.3.6) - strscan + rexml (3.3.9) rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) @@ -55,7 +54,6 @@ GEM rubocop-rake (0.6.0) rubocop (~> 1.0) ruby-progressbar (1.13.0) - strscan (3.1.0) unicode-display_width (2.5.0) PLATFORMS