ccru 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 66fdeba1e23df9b584d0b8f8f8ea2624497a2c570fce20b135a1b6f70babd39d
4
+ data.tar.gz: ec06b11b0728bf84c00f49770829b345ce62f7d95748885e82a54d418160b748
5
+ SHA512:
6
+ metadata.gz: d06a3bcb1bad991407f3b367bc9ccdd7fa7bbd5d329c148c1524d5434847c572cfcc1a65e790a0465e14da5c1367c9a6ed5d0eaa5d7f18286d3b4bb2a3df509a
7
+ data.tar.gz: 46298743b4620789e3a3452f12994dd4e69ba5492145dcee2101372b7a599fe1fa9c892a209f6d5e26f1302b71ce35c8f0987f3a2dcbc95b738555c8ac4895cf
@@ -0,0 +1,27 @@
1
+ name: Ruby
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ pull_request:
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ name: Ruby ${{ matrix.ruby }}
14
+ strategy:
15
+ matrix:
16
+ ruby:
17
+ - '3.3.0'
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - name: Set up Ruby
22
+ uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: ${{ matrix.ruby }}
25
+ bundler-cache: true
26
+ - name: Run the default task
27
+ run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.0
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ Style/StringLiterals:
7
+ EnforcedStyle: double_quotes
8
+
9
+ Style/StringLiteralsInInterpolation:
10
+ EnforcedStyle: double_quotes
11
+
12
+ Metrics/ClassLength:
13
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,79 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.0] - 2025-08-29
6
+
7
+ ### Added
8
+ - **JavaScript ES6 Syntax Checking**: New feature to detect ES6+ syntax violations in JavaScript files
9
+ - **Multi-language Support**: Now supports both Ruby (.rb) and JavaScript (.js) files
10
+ - **File Type Detection**: Automatic detection of file types and appropriate linter selection
11
+ - **ES6 Violation Rules**: Built-in detection for:
12
+ - Arrow functions (`=>`)
13
+ - const/let declarations
14
+ - Template literals (`` `string ${var}` ``)
15
+ - Destructuring assignments
16
+ - Spread operator (`...`)
17
+ - ES6 classes
18
+ - ES6 modules (import/export)
19
+ - Default parameters
20
+ - Rest parameters
21
+ - **JavaScript Code Quality Rules**: Comprehensive code quality checking including:
22
+ - **Code Style & Formatting**:
23
+ - Trailing whitespace detection
24
+ - Missing final newline detection
25
+ - Line length checking (120 characters)
26
+ - **Best Practices**:
27
+ - Console statement warnings (console.log, console.debug, etc.)
28
+ - Eval usage errors (security risk)
29
+ - With statement errors (deprecated)
30
+ - Document write warnings (performance/security)
31
+ - **Potential Bug Detection**:
32
+ - Loose equality warnings (== vs ===, allows == null/undefined)
33
+ - Loose inequality warnings (!= vs !==)
34
+ - Unused variable detection
35
+ - Missing semicolon detection (with smart detection)
36
+ - Inline comment warnings
37
+ - **Performance & Security**:
38
+ - innerHTML usage warnings (XSS risk)
39
+ - Global variable pollution warnings
40
+ - **ERB Template Support**: Custom ERB linter for convention checking:
41
+ - Proper spacing validation (`<% code %>` vs `<%code%>`)
42
+ - Comment format validation (`<%# comment %>` vs `<% # comment %>`)
43
+ - Output spacing validation (`<%= code %>` vs `<%=code%>`)
44
+ - **Enhanced CLI Options**: Support for RuboCop options via `--` separator
45
+ - **Improved Output Format**: Consistent violation reporting across all linters
46
+
47
+ ### Changed
48
+ - **Architecture Refactor**: Restructured code to support multiple linters
49
+ - **Module Organization**: Clean separation of concerns with dedicated modules:
50
+ - `LinterRunner`: Main orchestration module
51
+ - `RuboCopRunner`: Ruby file processing
52
+ - `JavaScriptLinterRunner`: JavaScript file processing
53
+ - `ErbLinterRunner`: ERB template processing
54
+ - **Git Diff Enhancement**: Extended to detect and process multiple file types
55
+ - **Violation Formatting**: Unified output format for all linter types
56
+
57
+ ### Technical Improvements
58
+ - **Modular Design**: Clean separation of concerns with dedicated modules
59
+ - **Extensible Architecture**: Easy to add support for new file types and linters
60
+ - **Backward Compatibility**: Existing Ruby-only functionality preserved
61
+ - **Performance**: Maintains the same fast git-diff-based approach
62
+ - **Error Handling**: Improved error handling and file reading safety
63
+
64
+ ### New Command Line Options
65
+ - Support for passing RuboCop options: `bundle exec ccru -- --auto-correct`
66
+
67
+ ### Notes
68
+ - Staged changes support is implemented in the code but not exposed via command line options
69
+ - Base branch can only be specified via environment variable `CCRU_BASE`
70
+
71
+ ## [0.1.0] - 2024-12-19
72
+
73
+ ### Added
74
+ - Initial release
75
+ - RuboCop integration for Ruby files
76
+ - Git diff-based change detection
77
+ - Line-level filtering for modified files
78
+ - Base branch specification via environment variable
79
+ - Basic file handling and offense printing
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in ccru.gemspec
6
+ gemspec
7
+
8
+ gem "pry", "0.15.2"
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ccru (0.1.0)
5
+ rubocop (>= 0.5, < 2.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.3)
11
+ coderay (1.1.3)
12
+ json (2.13.2)
13
+ language_server-protocol (3.17.0.5)
14
+ lint_roller (1.1.0)
15
+ method_source (1.1.0)
16
+ parallel (1.27.0)
17
+ parser (3.3.9.0)
18
+ ast (~> 2.4.1)
19
+ racc
20
+ prism (1.4.0)
21
+ pry (0.15.2)
22
+ coderay (~> 1.1)
23
+ method_source (~> 1.0)
24
+ racc (1.8.1)
25
+ rainbow (3.1.1)
26
+ regexp_parser (2.11.2)
27
+ rubocop (1.79.2)
28
+ json (~> 2.3)
29
+ language_server-protocol (~> 3.17.0.2)
30
+ lint_roller (~> 1.1.0)
31
+ parallel (~> 1.10)
32
+ parser (>= 3.3.0.2)
33
+ rainbow (>= 2.2.2, < 4.0)
34
+ regexp_parser (>= 2.9.3, < 3.0)
35
+ rubocop-ast (>= 1.46.0, < 2.0)
36
+ ruby-progressbar (~> 1.7)
37
+ unicode-display_width (>= 2.4.0, < 4.0)
38
+ rubocop-ast (1.46.0)
39
+ parser (>= 3.3.7.2)
40
+ prism (~> 1.4)
41
+ ruby-progressbar (1.13.0)
42
+ unicode-display_width (3.1.5)
43
+ unicode-emoji (~> 4.0, >= 4.0.4)
44
+ unicode-emoji (4.0.4)
45
+
46
+ PLATFORMS
47
+ arm64-darwin-24
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ ccru!
52
+ pry (= 0.15.2)
53
+
54
+ BUNDLED WITH
55
+ 2.5.22
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # ccru – Code Changed Rules (RuboCop + ES6 Syntax + ERB Checker)
2
+
3
+ RuboCop on diff for Ruby files + ES6 syntax checker for JavaScript files + ERB template support: lint only what's new/changed
4
+
5
+ ## Install
6
+
7
+ ```ruby
8
+ group :development, :test
9
+ gem 'ccru', require: false
10
+ end
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Basic Usage
16
+
17
+ ```bash
18
+ # Check changed Ruby, JavaScript, and ERB files in git diff (compared to merge-base)
19
+ bundle exec ccru
20
+
21
+ # Check specific files
22
+ bundle exec ccru path/to/file.rb path/to/script.js path/to/template.erb
23
+ ```
24
+
25
+ ### Environment Variables
26
+
27
+ ```bash
28
+ # Set default base branch for git diff comparison
29
+ export CCRU_BASE=develop
30
+ bundle exec ccru
31
+ ```
32
+
33
+ ## Supported File Types
34
+
35
+ - **Ruby files**: `.rb` - Checked with RuboCop
36
+ - **JavaScript files**: `.js` - Checked for ES6 syntax violations + code quality
37
+ - **ERB templates**: `.erb` - Checked with custom ERB linter for conventions
38
+
39
+ ## ERB Template Support
40
+
41
+ ### Ruby ERB Templates (`.erb`)
42
+ - Automatically checks ERB conventions and formatting
43
+ - Validates proper spacing: `<% code %>` instead of `<%code%>`
44
+ - Checks comment format: `<%# comment %>` instead of `<% # comment %>`
45
+ - Ensures proper output spacing: `<%= code %>` instead of `<%=code%>`
46
+ - Maps violations back to original ERB line numbers
47
+
48
+ ## JavaScript ES6 Violations Checked
49
+
50
+ The following ES6+ syntax patterns are detected and flagged as errors:
51
+
52
+ - **Arrow functions**: `=>` syntax
53
+ - **const/let**: `const` and `let` declarations
54
+ - **Template literals**: `` `string ${variable}` ``
55
+ - **Destructuring**: `{ prop } = obj` assignments
56
+ - **Spread operator**: `...` syntax
57
+ - **ES6 classes**: `class` declarations
58
+ - **ES6 modules**: `import`/`export` statements
59
+ - **Default parameters**: `function(param = value)`
60
+ - **Rest parameters**: `function(...args)`
61
+
62
+ ## JavaScript Code Quality Rules
63
+
64
+ ### Code Style & Formatting
65
+ - **Trailing whitespace**: Detects spaces at end of lines
66
+ - **Missing final newline**: Ensures files end with newline
67
+ - **Line length**: Maximum line length checking (120 characters)
68
+
69
+ ### Best Practices
70
+ - **Console statements**: Warns about `console.log`, `console.debug`, etc.
71
+ - **Eval usage**: Errors on dangerous `eval()` calls
72
+ - **With statement**: Errors on deprecated `with` statements
73
+ - **Document write**: Warns about `document.write()` usage
74
+
75
+ ### Potential Bugs
76
+ - **Loose equality**: Warns about `==` vs `===` usage (allows `== null` and `== undefined`)
77
+ - **Loose inequality**: Warns about `!=` vs `!==` usage
78
+ - **Unused variables**: Detects potentially unused variable declarations
79
+ - **Missing semicolons**: Identifies missing semicolons (with smart detection)
80
+ - **Inline comments**: Warns about inline comments at end of code lines
81
+
82
+ ### Performance Issues
83
+ - **innerHTML usage**: Warns about potential XSS vulnerabilities
84
+ - **Global variables**: Warns about global scope pollution
85
+
86
+ ## License
87
+
88
+ This project is licensed under the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
5
+
6
+ RuboCop::RakeTask.new
7
+
8
+ task default: :rubocop
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "ccru"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/ccru.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/ccru/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "ccru"
7
+ spec.version = Ccru::VERSION
8
+ spec.authors = ["thucpt"]
9
+ spec.email = ["thucpt@zigexn.vn"]
10
+
11
+ spec.summary = "Multi-language linter wrapper that checks only changed lines/files (Ruby, JavaScript, ERB)"
12
+ spec.description = "Checks only new files fully and modified files only for changed lines based on git diff.\
13
+ Supports Ruby (RuboCop), JavaScript (ES6 syntax + code quality), and ERB templates (conventions)."
14
+ spec.homepage = "https://github.com/thuczige/ccru"
15
+ spec.required_ruby_version = ">= 2.0"
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/thuczige/ccru"
21
+ spec.metadata["changelog_uri"] = "https://github.com/thuczige/ccru/blob/main/CHANGELOG.md"
22
+ spec.metadata["rubygems_mfa_required"] = "true"
23
+
24
+ # Specify which files should be added to the gem when it is released.
25
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
26
+ File.basename(__FILE__)
27
+ spec.files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ # spec.add_dependency "example-gem", "~> 1.0"
34
+
35
+ spec.add_dependency "rubocop", ">= 0.5", "< 2.0"
36
+
37
+ # For more information and examples about making a new gem, check out our
38
+ # guide at: https://bundler.io/guides/creating_gem.html
39
+ end
data/exe/ccru ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require "ccru"
3
+
4
+ # CLI will be autoloaded when accessed
5
+ exit Ccru::CLI.new.run(ARGV)
data/lib/ccru/cli.rb ADDED
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "git_diff"
4
+ require_relative "linter_runner"
5
+
6
+ require "pry"
7
+
8
+ module Ccru
9
+ # Command-line interface for ccru
10
+ class CLI
11
+ def run(argv)
12
+ puts "\n[START] #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}\n\n"
13
+ opts = { base: ENV.fetch("CCRU_BASE", nil), staged: false, files: argv }
14
+
15
+ # Check if there are specific files provided
16
+ return run_on_specific_files(opts[:files]) if opts[:files] && opts[:files].any?
17
+
18
+ files = Ccru::GitDiff.changed_files(opts[:base], opts[:staged])
19
+ return no_changes if files.empty?
20
+
21
+ run_on_files(files)
22
+ end
23
+
24
+ private
25
+
26
+ include LinterRunner
27
+
28
+ def run_on_specific_files(file_paths)
29
+ has_errors = false
30
+
31
+ file_paths.each do |path|
32
+ result = process_specific_file(path)
33
+ has_errors = true if result == 1
34
+ end
35
+
36
+ return 1 if has_errors
37
+
38
+ puts_all_ok
39
+ 0
40
+ end
41
+
42
+ def process_specific_file(path)
43
+ return 0 unless valid_file?(path)
44
+
45
+ file_type = FileTypeDetector.file_type(path)
46
+ meta = { type: :new, lines: nil, file_type: file_type }
47
+
48
+ run_linter_file(path, meta)
49
+ end
50
+
51
+ def valid_file?(path)
52
+ File.exist?(path) && FileTypeDetector.supported_file?(path)
53
+ end
54
+
55
+ def no_changes
56
+ puts_all_ok
57
+ 0
58
+ end
59
+
60
+ def run_on_files(files)
61
+ has_errors = false
62
+
63
+ files.each do |path, meta|
64
+ result = process_file(path, meta)
65
+ has_errors = true if result == 1
66
+ end
67
+
68
+ return 1 if has_errors
69
+
70
+ puts_all_ok
71
+ 0
72
+ end
73
+
74
+ def process_file(path, meta)
75
+ return run_linter_file(path, meta) if meta[:type] == :new
76
+
77
+ run_linter_filtered(path, meta)
78
+ end
79
+
80
+ def puts_all_ok
81
+ puts "ccru: All OK - No violations found\n\n"
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "usual_linter"
4
+
5
+ module Ccru
6
+ # ERB linter that checks basic ERB conventions
7
+ class ErbLinter
8
+ include UsualLinter
9
+
10
+ def initialize
11
+ @offenses = []
12
+ end
13
+
14
+ # ERB convention violations
15
+ ERB_VIOLATIONS = {
16
+ # Bad comment format: <% # comment %> instead of <%# comment %>
17
+ bad_comment_format: {
18
+ pattern: /<%\s+#[^%]*%>/,
19
+ message: "Use <%# comment %> instead of <% # comment %> for ERB comments",
20
+ cop_name: "BadCommentFormat",
21
+ severity: "error"
22
+ },
23
+ # Bad spacing: <%foo%> instead of <% foo %>
24
+ bad_spacing: {
25
+ pattern: /<%(?!=)[^\s#][^%]*%>/,
26
+ message: "<% your_code %> for better readability",
27
+ cop_name: "BadSpacing",
28
+ severity: "warning"
29
+ },
30
+ # Bad output spacing: <%=foo%> instead of <%= foo %>
31
+ bad_output_spacing: {
32
+ pattern: /<%=[^\s][^%]*%>/,
33
+ message: "<%= your_code %> for better readability",
34
+ cop_name: "BadOutputSpacing",
35
+ severity: "warning"
36
+ },
37
+ # Bad comment spacing: <%#foo%> instead of <%# foo %>
38
+ bad_comment_spacing: {
39
+ pattern: /<%#[^\s][^%]*%>/,
40
+ message: "<%# your_comment %> for better readability",
41
+ cop_name: "BadCommentSpacing",
42
+ severity: "warning"
43
+ }
44
+ }.freeze
45
+
46
+ def lint_file(content)
47
+ check_final_newline(content)
48
+ check_erb_conventions(content)
49
+ check_trailing_whitespace(content)
50
+
51
+ @offenses
52
+ end
53
+
54
+ def lint_filtered(content, changed_lines)
55
+ changed_lines.each do |line_number|
56
+ line_content = content.lines[line_number - 1]
57
+ next unless line_content
58
+
59
+ check_line_conventions(line_content, line_number)
60
+ end
61
+
62
+ @offenses
63
+ end
64
+
65
+ def check_erb_conventions(content)
66
+ content.lines.each_with_index do |line_content, index|
67
+ next if line_content.strip.empty?
68
+
69
+ line_number = index + 1
70
+ check_line_conventions(line_content, line_number)
71
+ end
72
+ end
73
+
74
+ def check_line_conventions(line_content, line_number)
75
+ ERB_VIOLATIONS.each do |rule_name, rule|
76
+ next if line_content.match(rule[:pattern]).nil?
77
+
78
+ add_offense(rule_name, rule, line_content, line_number)
79
+ break
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "erb_linter"
4
+ require_relative "file_handler"
5
+ require_relative "offense_printer"
6
+
7
+ module Ccru
8
+ # ERB linter runner methods
9
+ module ErbLinterRunner
10
+ include FileHandler
11
+ include OffensePrinter
12
+
13
+ def run_erb_linter_file(path)
14
+ content = safe_read_file(path)
15
+ return 0 unless content
16
+
17
+ erb_linter = ErbLinter.new
18
+ offenses = erb_linter.lint_file(content)
19
+ return 0 if offenses.empty?
20
+
21
+ print_offenses(path, offenses)
22
+ 1
23
+ end
24
+
25
+ def run_erb_linter_filtered(path, meta)
26
+ content = safe_read_file(path)
27
+ return 0 unless content && meta[:lines].any?
28
+
29
+ erb_linter = ErbLinter.new
30
+ offenses = erb_linter.lint_filtered(content, meta[:lines])
31
+ return 0 if offenses.empty?
32
+
33
+ print_offenses(path, offenses)
34
+ 1
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ccru
4
+ # Handles file operations for RuboCop
5
+ module FileHandler
6
+ def safe_read_file(path)
7
+ File.read(path)
8
+ rescue StandardError
9
+ nil
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ccru
4
+ # Detects file types and determines which linter to use
5
+ module FileTypeDetector
6
+ SUPPORTED_EXTENSIONS = {
7
+ ".rb" => :ruby,
8
+ ".js" => :javascript,
9
+ ".erb" => :erb
10
+ }.freeze
11
+
12
+ class << self
13
+ def supported_file?(path)
14
+ SUPPORTED_EXTENSIONS.key?(File.extname(path))
15
+ end
16
+
17
+ def file_type(path)
18
+ extension = File.extname(path)
19
+ SUPPORTED_EXTENSIONS[extension]
20
+ end
21
+
22
+ def ruby_file?(path)
23
+ file_type(path) == :ruby
24
+ end
25
+ end
26
+ end
27
+ end