Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Migrate analyzer script to Ruby #251

Draft
wants to merge 30 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6967230
Begin to write a Ruby script, more robust than Shell script
gaelfoppolo Aug 31, 2019
554214c
Update Options parser to support report argument.
gaelfoppolo Sep 1, 2019
83e3b0e
Add Helper class to handle common tasks
gaelfoppolo Sep 1, 2019
1d909f6
Add SonarScanner class
gaelfoppolo Sep 1, 2019
7b9c5eb
Move main to Analyzer class
gaelfoppolo Sep 1, 2019
6bf4ca5
Move tools to folder and add Lizard
gaelfoppolo Sep 1, 2019
95d7465
Add SonarRunner class
gaelfoppolo Sep 1, 2019
2640ac0
Add XCPretty & Slather class
gaelfoppolo Sep 1, 2019
0a831ca
Add UnitTests & OCLint class
gaelfoppolo Sep 1, 2019
17a0fa1
Update error messages
gaelfoppolo Sep 1, 2019
1057396
Add more check when parsing project properties, early exit
gaelfoppolo Sep 1, 2019
b311938
Add new tools in Analyzer
gaelfoppolo Sep 1, 2019
92f3d1d
Add FauxPas class
gaelfoppolo Sep 1, 2019
94c31fc
Fix Hash key name
gaelfoppolo Sep 1, 2019
41bca61
Rework Tool to make it support list of commands
gaelfoppolo Sep 1, 2019
8936027
Fix hash key
gaelfoppolo Sep 1, 2019
2d0431f
Add JSONCompilationDatabase class
gaelfoppolo Sep 1, 2019
881982a
Add options to disable tools
gaelfoppolo Sep 1, 2019
7dfb62a
Add option to disable upload (test purpose)
gaelfoppolo Sep 1, 2019
02c241c
Add ability to skip tool if not installed
gaelfoppolo Sep 1, 2019
75cc9a1
Check number of ObjC files
gaelfoppolo Sep 1, 2019
f2deed6
Export some variables to constants
gaelfoppolo Sep 3, 2019
0fe10e1
Fix variable
gaelfoppolo Sep 3, 2019
3989160
Fix naming and remove some verbose parameters
gaelfoppolo Sep 8, 2019
4fe89da
Begin to create gem
gaelfoppolo Sep 8, 2019
7a2fa53
Fix paths
gaelfoppolo Sep 8, 2019
f680fe5
Add executable
gaelfoppolo Sep 8, 2019
f67f6bc
Fix executable and add version
gaelfoppolo Sep 8, 2019
78c472c
Remove xcpretty fork
gaelfoppolo Sep 8, 2019
6becc6b
Fix gemspec and add a quick README
gaelfoppolo Sep 8, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions analyzer/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source 'https://rubygems.org'

gemspec
33 changes: 33 additions & 0 deletions analyzer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# README

# Installation

```sh
gem build analyzer.gemspec --output=analyzer.gem
```
```sh
gem install analyzer.gem
```

# Usage

```sh
analyzer
```

To see available options: `analyzer -h`

# Requirements

* Xcode
* XCPretty
* Slather
* SwiftLint
* Lizard
* OCLint
* FauxPas
* SonarScanner

## Behavior

When a tool is not installed, the analyzer will gracefully skip it.
24 changes: 24 additions & 0 deletions analyzer/analyzer.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'version'

Gem::Specification.new do |s|
s.name = "sonar-swift-analyzer"
s.version = AnalyzerV::VERSION
s.summary = "SonarSwift plugin analyzer"
s.authors = ["Gaël Foppolo"]

s.description = "SonarSwift plugin analyzer"
s.license = 'MIT'

s.required_ruby_version = '>= 2.0.0'

s.files = `git ls-files`.split($/)
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
s.require_paths = ["lib"]

s.add_runtime_dependency 'java-properties', "= 0.2.0"
s.add_runtime_dependency 'slather', "= 2.4.7"
s.add_runtime_dependency 'xcpretty', "= 0.3.0"

end
5 changes: 5 additions & 0 deletions analyzer/bin/analyzer
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env ruby

require 'analyzer'

Analyzer.new.run
78 changes: 78 additions & 0 deletions analyzer/lib/analyzer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env ruby
require 'ostruct'
require 'logging'
require 'tools/swiftlint'
require 'tools/lizard'
require 'tools/sonar_scanner'
require 'tools/sonar_runner'
require 'tools/unit_tests'
require 'tools/slather'
require 'tools/oclint'
require 'tools/fauxpas'
require 'tools/json_compilation_database'
require 'options'
require 'properties_reader'
require 'helper'

# Entry point of the script.
#
# It reads the configurations, run all analytic tools and send reports
# to Sonar.
class Analyzer
include Logging

def initialize
@options = OpenStruct.new
# list of tools by default
@options.tools = [JSONCompilationDatabase, UnitTests, Slather, SwiftLint, Lizard, OCLint, FauxPas]
# reporter by default
@options.reporter = SonarScanner
# upload results to SonarQube by default
@options.upload = true
# upload results to SonarQube by default
@options.path = 'sonar-project.properties'
# report folder
@options.report_folder = 'sonar-reports'

@helper = Helper.new

end

def run

# read CLI arguments and update configuration
Options.new.parse(ARGV, @options)

# Initiate reports
@helper.bootstrap_reports(@options.report_folder)

# Read Sonar project properties
@properties = SonarPropertiesReader.new(@options.path).read

tools
reporter if @options.upload

end

private
def tools
# Filter tools by availability
@options.tools = @helper.available(@options.tools)

# Run tools
@options.tools.each do |tool|
tool.new(@properties, @options).run
end
end

private
def reporter
# Filter reporters by availability
@options.reporter = @helper.available(@options.reporter)

# Send reports
@options.reporter.new(@properties, @options).run unless @options.reporter.nil?

end

end
8 changes: 8 additions & 0 deletions analyzer/lib/canfail.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Adds a fatal_error method that logs and exit.
# This module expects the Logging module included.
module CanFail
def fatal_error(msg)
logger.error(msg)
exit(false)
end
end
58 changes: 58 additions & 0 deletions analyzer/lib/helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'fileutils'
require 'logging'

class Helper
include Logging

# Put default xml files with no tests and no coverage. This is needed to
# ensure a file is present, either the Xcode build test step worked or
# not. Without this, Sonar Scanner will fail uploading results.
def bootstrap_reports(folder)
reports_folder(folder)
mandatory_reports(folder)
end

# Check each program is available and return an updated list.
def available(programs)
case programs
when Array
programs.select do |program|
_available(program)
end
else
if _available(programs) then programs else nil end

end
end

# Check program is available and return an updated list.
private
def _available(program)
availability = program.command.values.reduce(true) { |available, tool|
toolAvailable = system("which #{tool} 2>&1 > /dev/null")
logger.warn("#{tool} is not found in PATH") if !toolAvailable
available &= toolAvailable
}
logger.warn("Skipping #{program}.") if !availability
availability
end

private
def mandatory_reports(folder)
logger.info('Creating default reports')
empty_test_report = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><testsuites name='AllTestUnits'></testsuites>"
File.write("#{folder}/TEST-report.xml", empty_test_report)

empty_coverage_report = "<?xml version='1.0' ?><!DOCTYPE coverage SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'><coverage><sources></sources><packages></packages></coverage>"
File.write("#{folder}/coverage.xml", empty_coverage_report)
end

private
def reports_folder(folder)
logger.info("Deleting and creating directory #{folder}")
FileUtils.rm_rf(folder)
Dir.mkdir(folder)
end

end

29 changes: 29 additions & 0 deletions analyzer/lib/logging.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'logger'

# Adds logging capability where included.
module Logging
def logger
@logger ||= Logging.logger_for(self.class.name)
end

# Use a hash class-ivar to cache a unique Logger per class:
@@loggers = {}
@@logger_level = Logger::INFO

class << self
def logger_for(classname)
@@loggers[classname] ||= configure_logger_for(classname)
end

def configure_logger_for(classname)
logger = Logger.new(STDOUT)
logger.progname = classname
logger.level = @@logger_level
logger
end

def logger_level=(level)
@@logger_level = level
end
end
end
82 changes: 82 additions & 0 deletions analyzer/lib/options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
require 'optparse'

class Options

REPORTER_ALIASES = { :scanner => "SonarScanner", :runner => "SonarRunner" }

#
# Return a structure describing the options.
#
def parse(args, options)
# The options specified on the command line will be updated in *options*.

opt_parser = OptionParser.new do |opts|
opts.banner = "Usage: main.rb [options]"

opts.separator ""
opts.separator "Specific options:"

# Optional
opts.on("-p", "--path PATH", String, "Path to Sonar properties file.") do |p|
options.path = p
end

# Optional
opts.on("-r", "--reporter [REPORTER]", REPORTER_ALIASES.keys, "Select Sonar reporter (scanner, runner)") do |r|
options.reporter = REPORTER_ALIASES[r]
end

opts.separator ""
opts.separator "Disable tools:"

# Optional
opts.on("--disable-swiftlint", "Disable SwiftLint") do |_|
options.tools.delete_at(options.tools.index(SwiftLint))
end

# Optional
opts.on("--disable-oclint", "Disable OCLint") do |_|
options.tools.delete_at(options.tools.index(JSONCompilationDatabase))
options.tools.delete_at(options.tools.index(OCLint))
end

# Optional
opts.on("--disable-slather", "Disable Slather") do |_|
options.tools.delete_at(options.tools.index(Slather))
end

# Optional
opts.on("--disable-lizard", "Disable Lizard") do |_|
options.tools.delete_at(options.tools.index(Lizard))
end

# Optional
opts.on("--disable-fauxpas", "Disable FauxPas") do |_|
options.tools.delete_at(options.tools.index(FauxPas))
end

# Optional
opts.on("--disable-upload", "Disable upload to SonarQube server") do |_|
options.upload = false
end

opts.separator ""
opts.separator "Common options:"

opts.on("-v", "--verbose", "Run verbosely") do |_|
Logging.logger_level = Logger::DEBUG
end

# No argument, shows at tail. This will print an options summary.
# Try it and see!
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
end

opt_parser.parse!(args)

end

end
Loading