diff --git a/analyzer/Gemfile b/analyzer/Gemfile new file mode 100644 index 00000000..cd8aa9e0 --- /dev/null +++ b/analyzer/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec \ No newline at end of file diff --git a/analyzer/README.md b/analyzer/README.md new file mode 100644 index 00000000..dc40239a --- /dev/null +++ b/analyzer/README.md @@ -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. \ No newline at end of file diff --git a/analyzer/analyzer.gemspec b/analyzer/analyzer.gemspec new file mode 100644 index 00000000..704c82e5 --- /dev/null +++ b/analyzer/analyzer.gemspec @@ -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 \ No newline at end of file diff --git a/analyzer/bin/analyzer b/analyzer/bin/analyzer new file mode 100755 index 00000000..9ddaea74 --- /dev/null +++ b/analyzer/bin/analyzer @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require 'analyzer' + +Analyzer.new.run \ No newline at end of file diff --git a/analyzer/lib/analyzer.rb b/analyzer/lib/analyzer.rb new file mode 100644 index 00000000..324d6831 --- /dev/null +++ b/analyzer/lib/analyzer.rb @@ -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 \ No newline at end of file diff --git a/analyzer/lib/canfail.rb b/analyzer/lib/canfail.rb new file mode 100644 index 00000000..8893d28c --- /dev/null +++ b/analyzer/lib/canfail.rb @@ -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 diff --git a/analyzer/lib/helper.rb b/analyzer/lib/helper.rb new file mode 100644 index 00000000..ba29f790 --- /dev/null +++ b/analyzer/lib/helper.rb @@ -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 = "" + File.write("#{folder}/TEST-report.xml", empty_test_report) + + empty_coverage_report = "" + 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 + diff --git a/analyzer/lib/logging.rb b/analyzer/lib/logging.rb new file mode 100644 index 00000000..82486339 --- /dev/null +++ b/analyzer/lib/logging.rb @@ -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 \ No newline at end of file diff --git a/analyzer/lib/options.rb b/analyzer/lib/options.rb new file mode 100644 index 00000000..f1d25e1f --- /dev/null +++ b/analyzer/lib/options.rb @@ -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 \ No newline at end of file diff --git a/analyzer/lib/properties_reader.rb b/analyzer/lib/properties_reader.rb new file mode 100644 index 00000000..570e9d22 --- /dev/null +++ b/analyzer/lib/properties_reader.rb @@ -0,0 +1,68 @@ +require 'logging' +require 'canfail' +require 'fileutils' +require 'java-properties' + +# SonarPropertiesReader reads and check the sonar-project.properties file. +# It also creates a Hash with all read properties. +class SonarPropertiesReader + include Logging + include CanFail + + def initialize(path) + fatal_error("No #{path} found") unless File.exist?(path) + @file = path + end + + # Read the Java properties file and return a Hash suitable for the script. + def read + logger.info('Reading Sonar project properties...') + properties = JavaProperties.load(@file) + options = read_properties(properties) + validate_settings!(options) + end + + private + + # Map the Java properties hash to more Ruby hash + def read_properties(properties) + options = {} + options[:project] = properties[:'sonar.swift.project'] + options[:workspace] = properties[:'sonar.swift.workspace'] + options[:sources] = properties[:'sonar.sources'] + options[:sources] = options[:sources].split(',') unless options[:sources].nil? + options[:scheme] = properties[:'sonar.swift.appScheme'] + options[:configuration] = properties[:'sonar.swift.appConfiguration'] + options[:simulator] = properties[:'sonar.swift.simulator'] + options[:exclude_from_coverage] = properties[:'sonar.swift.excludedPathsFromCoverage'] + options[:exclude_from_coverage] = options[:exclude_from_coverage].split(',') unless options[:exclude_from_coverage].nil? + options[:binary_names] = properties[:'sonar.coverage.binaryNames'] + options[:binary_names] = options[:binary_names].split(',') unless options[:binary_names].nil? + options[:skip_tests] = properties[:'sonar.swift.skipTests'] + options[:skip_tests] = options[:skip_tests].split(',') unless options[:skip_tests].nil? + options + end + + def validate_settings!(options) + fatal_error("No project or workspace specified in #{@file}") if (options[:workspace].nil? && options[:project].nil?) + check_file(options[:workspace]) + check_file(options[:project]) + fatal_error("No sources folder specified in #{@file}") if options[:sources].nil? + options[:sources].each do |source| + check_file(source) + end + fatal_error("No scheme specified in #{@file}") if options[:scheme].nil? + if options[:configuration].nil? + logger.warn('No build configuration set, defaulting to Debug') + options[:configuration] = 'Debug' + end + options + end + + def check_file(file) + unless file.nil? + fatal_error("#{file} not found") unless File.exist?(file) + end + end + +end \ No newline at end of file diff --git a/analyzer/lib/tools/fauxpas.rb b/analyzer/lib/tools/fauxpas.rb new file mode 100644 index 00000000..4df0f2f9 --- /dev/null +++ b/analyzer/lib/tools/fauxpas.rb @@ -0,0 +1,40 @@ +require 'tools/tool' + +class FauxPas < Tool + + @@REPORT_FILE = 'fauxpas.json'.freeze + + def self.command + { + fauxpas: 'fauxpas' + } + end + + def initialize(properties, options) + @workspace = properties[:workspace] + @project = properties[:project] + @scheme = properties[:scheme] + @report_folder = options.report_folder + super(properties, options) + end + + def run() + logger.info('Running...') + cmd = "#{self.class.command[:fauxpas]} check -o json #{@project}" + cmd += " --workspace #{@workspace}" unless @workspace.nil? + cmd += " --scheme #{@scheme}" + cmd += " -v " + cmd += if logger.level == Logger::DEBUG then "yes" else "no" end + cmd += " > #{@report_folder}/#{@@REPORT_FILE}" + logger.debug("Will run `#{cmd}`") + system(cmd) + end + + private + + def validate_settings! + # @workspace is optional + fatal_error('A project must be set in order run FauxPas') if @project.nil? + fatal_error('A scheme must be set in order run FauxPas') if @scheme.nil? + end +end \ No newline at end of file diff --git a/analyzer/lib/tools/json_compilation_database.rb b/analyzer/lib/tools/json_compilation_database.rb new file mode 100644 index 00000000..e1b20987 --- /dev/null +++ b/analyzer/lib/tools/json_compilation_database.rb @@ -0,0 +1,46 @@ +require 'tools/tool' + +class JSONCompilationDatabase < Tool + + @@TIMEOUT = '360'.freeze + @@COMPILE_COMMANDS_FILE = 'compile_commands.json'.freeze + @@XCODEBUILD_FILE = 'xcodebuild.log'.freeze + + def self.command + { + xcodebuild: 'xcodebuild', + xcpretty: 'xcpretty' + } + end + + def initialize(properties, options) + @workspace = properties[:workspace] + @project = properties[:project] + @scheme = properties[:scheme] + @simulator = properties[:simulator] + super(properties, options) + end + + def run + logger.info('Running ...') + cmd = "#{self.class.command[:xcodebuild]} clean build" + cmd += " -workspace \"#{@workspace}\"" unless @workspace.nil? + cmd += " -project \"#{@project}\"" unless !@workspace.nil? + cmd += " -scheme \"#{@scheme}\"" + cmd += " -destination '#{@simulator}' -destination-timeout #{@@TIMEOUT} COMPILER_INDEX_STORE_ENABLE=NO" unless @simulator.nil? + cmd += " | tee #{@@XCODEBUILD_FILE}" + cmd += " | #{self.class.command[:xcpretty]} -r json-compilation-database -o #{@@COMPILE_COMMANDS_FILE}" + logger.debug("Will run `#{cmd}`") + system(cmd) + end + + private + + def validate_settings! + # @workspace is optional + fatal_error('A project must be set in order to compute JSON compilation database') if @project.nil? + fatal_error('A scheme must be set in order to compute JSON compilation database') if @scheme.nil? + logger.warn('No simulator specified') if @simulator.nil? + end + +end \ No newline at end of file diff --git a/analyzer/lib/tools/lizard.rb b/analyzer/lib/tools/lizard.rb new file mode 100644 index 00000000..a27bff96 --- /dev/null +++ b/analyzer/lib/tools/lizard.rb @@ -0,0 +1,38 @@ +require 'tools/tool' + +# Lizard computes the code complexity. +# +# @see http://www.lizard.ws +class Lizard < Tool + + @@REPORT_FILE = 'lizard-report.xml'.freeze + + def self.command + { + lizard: 'lizard' + } + end + + def initialize(properties, options) + @sources = properties[:sources] + @report_folder = options.report_folder + super(properties, options) + end + + def run() + logger.info('Running...') + cmd = "#{self.class.command[:lizard]} --xml" + @sources.each do |source| + cmd += " \"#{source}\"" + end + cmd += " > #{@report_folder}/#{@@REPORT_FILE}" + logger.debug("Will run `#{cmd}`") + system(cmd) + end + + private + + def validate_settings! + fatal_error('Sources must be set in order to compute Lizard complexity') if @sources.nil? + end +end \ No newline at end of file diff --git a/analyzer/lib/tools/oclint.rb b/analyzer/lib/tools/oclint.rb new file mode 100644 index 00000000..b25a0414 --- /dev/null +++ b/analyzer/lib/tools/oclint.rb @@ -0,0 +1,50 @@ +require 'tools/tool' +require 'fileutils' + +class OCLint < Tool + + @@MAX_PRIORITY = 10000 + @@LONG_LINE_THRESHOLD = 250 + @@REPORT_FILE = '-oclint.xml'.freeze + + def self.command + { + oclint: 'oclint-json-compilation-database' + } + end + + def initialize(properties, options) + @sources = properties[:sources] + @report_folder = options.report_folder + super(properties, options) + end + + def run() + logger.info('Running...') + @sources.each do |source| + + next unless `find \"#{source}/\" -name '*.m' | wc -l | tr -d ' ' 2>&1` != 0 + + report_name = "#{source.tr(' ', '_')}#{@@REPORT_FILE}" + + cmd = "#{self.class.command[:oclint]}" + cmd += " -v" if logger.level == Logger::DEBUG + cmd += " --include #{source}" + cmd += " -- -rc LONG_LINE=#{@@LONG_LINE_THRESHOLD}" + cmd += " -max-priority-1 #{@@MAX_PRIORITY} -max-priority-2 #{@@MAX_PRIORITY} -max-priority-3 #{@@MAX_PRIORITY}" + cmd += " -report-type pmd" + cmd += " -o #{@report_folder}/#{report_name}" + + logger.debug("Will run `#{cmd}`") + system(cmd) + end + + end + + private + + def validate_settings! + fatal_error('Sources must be set in order to run OCLint') if @sources.nil? + fatal_error("compile_commands.json not found") unless File.exist?("compile_commands.json") + end +end \ No newline at end of file diff --git a/analyzer/lib/tools/slather.rb b/analyzer/lib/tools/slather.rb new file mode 100644 index 00000000..da7d76d8 --- /dev/null +++ b/analyzer/lib/tools/slather.rb @@ -0,0 +1,59 @@ +require 'fileutils' +require 'tools/tool' + +class Slather < Tool + + @@REPORT_FILE_DEFAULT = 'cobertura.xml'.freeze + @@REPORT_FILE = 'coverage-swift.xml'.freeze + + def self.command + { + slather: 'slather' + } + end + + def initialize(properties, options) + @workspace = properties[:workspace] + @project = properties[:project] + @scheme = properties[:scheme] + @exclude_from_coverage = properties[:exclude_from_coverage] + @binary_names = properties[:binary_names] + @report_folder = options.report_folder + super(properties, options) + end + + def run() + logger.info('Running...') + + cmd = "#{self.class.command[:slather]} coverage" + cmd += " --verbose" if logger.level == Logger::DEBUG + unless @binary_names.nil? + @binary_names.each do |binary| + cmd += " --binary-basename \"#{binary}\"" + end + end + unless @exclude_from_coverage.nil? + @exclude_from_coverage.each do |exclusion| + cmd += " -i \"#{exclusion}\"" + end + end + cmd += " --input-format profdata --cobertura-xml --output-directory #{@report_folder}" + cmd += " --workspace #{@workspace}" unless @workspace.nil? + cmd += " --scheme #{@scheme} #{@project}" + logger.debug("Will run `#{cmd}`") + system(cmd) + + FileUtils.mv("#{@report_folder}/#{@@REPORT_FILE_DEFAULT}", "#{@report_folder}/#{@@REPORT_FILE}") + + end + + private + + def validate_settings! + # @workspace is optional + fatal_error('A project must be set in order to run Slather') if @project.nil? + fatal_error('A scheme must be set in order to run Slather') if @scheme.nil? + # @exclude_from_coverage is optional + # @binary_names is optional + end +end \ No newline at end of file diff --git a/analyzer/lib/tools/sonar_runner.rb b/analyzer/lib/tools/sonar_runner.rb new file mode 100644 index 00000000..368eecbd --- /dev/null +++ b/analyzer/lib/tools/sonar_runner.rb @@ -0,0 +1,21 @@ +require 'tools/tool' + +class SonarRunner < Tool + def self.command + { + runner: 'sonar-runner' + } + end + + def initialize(properties, options) + super(properties, options) + end + + def run() + logger.info('Running...') + cmd = "#{self.class.command[:runner]}" + logger.debug("Will run `#{cmd}`") + system(cmd) + end + +end \ No newline at end of file diff --git a/analyzer/lib/tools/sonar_scanner.rb b/analyzer/lib/tools/sonar_scanner.rb new file mode 100644 index 00000000..5b586020 --- /dev/null +++ b/analyzer/lib/tools/sonar_scanner.rb @@ -0,0 +1,21 @@ +require 'tools/tool' + +class SonarScanner < Tool + def self.command + { + scanner: 'sonar-scanner' + } + end + + def initialize(properties, options) + super(properties, options) + end + + def run() + logger.info('Running...') + cmd = "#{self.class.command[:scanner]}" + logger.debug("Will run `#{cmd}`") + system(cmd) + end + +end \ No newline at end of file diff --git a/analyzer/lib/tools/swiftlint.rb b/analyzer/lib/tools/swiftlint.rb new file mode 100644 index 00000000..589a712a --- /dev/null +++ b/analyzer/lib/tools/swiftlint.rb @@ -0,0 +1,43 @@ +require 'tools/tool' + +# SwiftLint checks code style and conventions. +# +# It is mainly based on the [Swift Style +# Guide](https://github.com/github/swift-style-guide) and it may also be +# used to enforce custom conventions. +# +# https://github.com/realm/SwiftLint +class SwiftLint < Tool + + @@REPORT_FILE = '-swiftlint.txt'.freeze + + def self.command + { + swiftlint: 'swiftlint' + } + end + + def initialize(properties, options) + @sources = properties[:sources] + @report_folder = options.report_folder + super(properties, options) + end + + def run() + logger.info('Running...') + @sources.each do |source| + report_name = "#{source.tr(' ', '_')}#{@@REPORT_FILE}" + cmd = "#{self.class.command[:swiftlint]} lint --path \"#{source}\"" + cmd += " --quiet" unless logger.level == Logger::DEBUG + cmd += " > #{@report_folder}/#{report_name}" + logger.debug("Will run `#{cmd}`") + system(cmd) + end + end + + private + + def validate_settings! + fatal_error('Sources must be set in order to lint') if @sources.nil? + end +end \ No newline at end of file diff --git a/analyzer/lib/tools/tool.rb b/analyzer/lib/tools/tool.rb new file mode 100644 index 00000000..19b5c8c5 --- /dev/null +++ b/analyzer/lib/tools/tool.rb @@ -0,0 +1,25 @@ +require 'logging' +require 'canfail' + +# A base class for tool wrappers. +# +# Mainly defines a common interface + includes some useful modules. +class Tool + include Logging + include CanFail + + def self.command + end + + def initialize(properties, options) + validate_settings! + end + + def run + end + + protected + + def validate_settings! + end +end \ No newline at end of file diff --git a/analyzer/lib/tools/unit_tests.rb b/analyzer/lib/tools/unit_tests.rb new file mode 100644 index 00000000..dc002a2f --- /dev/null +++ b/analyzer/lib/tools/unit_tests.rb @@ -0,0 +1,61 @@ +require 'tools/tool' + +# Runs unit tests using Xcode with `xcodebuild` +class UnitTests < Tool + + @@TIMEOUT = '60'.freeze + @@REPORT_FILE = 'TEST-report.xml' + + def self.command + { + xcodebuild: 'xcodebuild', + xcpretty: 'xcpretty' + } + end + + def initialize(properties, options) + @workspace = properties[:workspace] + @project = properties[:project] + @scheme = properties[:scheme] + @configuration = properties[:configuration] + @simulator = properties[:simulator] + @exclude_from_coverage = properties[:exclude_from_coverage] + @skip_tests = properties[:skip_tests] + @report_folder = options.report_folder + super(properties, options) + end + + def run + logger.info('Running...') + cmd = "#{self.class.command[:xcodebuild]} clean build-for-testing test" + cmd += " -workspace \"#{@workspace}\"" unless @workspace.nil? + cmd += " -project \"#{@project}\"" unless !@workspace.nil? + cmd += " -scheme \"#{@scheme}\"" + cmd += " -configuration \"#{@configuration}\"" + cmd += " -enableCodeCoverage YES" + cmd += " -destination '#{@simulator}' -destination-timeout #{@@TIMEOUT}" unless @simulator.nil? + unless @skip_tests.nil? + @skip_tests.each do |test| + cmd += " -skip-testing:#{test}" + end + end + cmd += " -quiet" unless logger.level == Logger::DEBUG + cmd += " | tee xcodebuild.log" + cmd += " | #{self.class.command[:xcpretty]} -t --report junit -o #{@report_folder}/#{@@REPORT_FILE}" + logger.debug("Will run `#{cmd}`") + system(cmd) + end + + private + + def validate_settings! + # @workspace is optional + fatal_error('A project must be set in order to run test') if @project.nil? + fatal_error('A scheme must be set in order to build and test the app') if @scheme.nil? + fatal_error('A configuration must be set in order to build and test the app') if @configuration.nil? + logger.warn('No simulator specified') if @simulator.nil? + # @exclude_from_coverage is optional + # @skip_tests is optional + end + +end \ No newline at end of file diff --git a/analyzer/lib/version.rb b/analyzer/lib/version.rb new file mode 100644 index 00000000..b552c37b --- /dev/null +++ b/analyzer/lib/version.rb @@ -0,0 +1,3 @@ +module AnalyzerV + VERSION = "0.0.1" +end diff --git a/sonar-project.properties b/sonar-project.properties deleted file mode 100755 index b793770f..00000000 --- a/sonar-project.properties +++ /dev/null @@ -1,108 +0,0 @@ -# -# Swift SonarQube Plugin - Enables analysis of Swift and Objective-C projects into SonarQube. -# Copyright © 2015 Backelite (${email}) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -# - -sonar.projectKey=prjKey -sonar.projectName=prjName -# Number version (can be found automatically in plist, just comment this line) -sonar.projectVersion=1.0 - -# Comment if you have a project with mixed ObjC / Swift -sonar.language=swift - -# Project description -sonar.projectDescription=prjDescription - -# Path to source directories -sonar.sources=SourceDir -# Path to test directories (comment if no test) -sonar.tests=TestDir - -# Destination Simulator to run surefire -# As string expected in destination argument of xcodebuild command -# Example = sonar.swift.simulator=platform=iOS Simulator,name=iPhone 6,OS=9.2 -sonar.swift.simulator=platform=iOS Simulator,name=iPhone X,OS=latest - -# Xcode project configuration (.xcodeproj) -# and use the later to specify which project(s) to include in the analysis (comma separated list) -# Specify either xcodeproj or xcodeproj + xcworkspace -#sonar.swift.project=MyPrj.xcodeproj -#sonar.swift.workspace=MyWrkSpc.xcworkspace - -# Scheme to build your application -sonar.swift.appScheme=MyScheme - -# Specify your appname when different from targeted scheme. -# Or when slather fails with 'No product binary found' -# You can also provide a list of framework names to analyse for coverage. -# This will be something like "myApp" or "myApp,myFramework" -# sonar.coverage.binaryNames=myApp,myFramework - -# Configuration to use for your scheme. if you do not specify that the default will be Debug -sonar.swift.appConfiguration=MyConfiguration - -########################## -# Optional configuration # -########################## -# Encoding of the source code -sonar.sourceEncoding=UTF-8 -# SCM -# sonar.scm.enabled=true -# sonar.scm.url=scm:git:http://xxx - -# JUnit report generated by run-sonar.sh is stored in sonar-reports/TEST-report.xml -# Change it only if you generate the file on your own -# The XML files have to be prefixed by TEST- otherwise they are not processed -# sonar.junit.reportsPath=sonar-reports/ - -# Lizard report generated by run-sonar.sh is stored in sonar-reports/lizard-report.xml -# Change it only if you generate the file on your own -# sonar.swift.lizard.report=sonar-reports/lizard-report.xml - -# Cobertura report generated by run-sonar.sh is stored in sonar-reports/coverage-swift.xml -# Change it only if you generate the file on your own -# sonar.swift.coverage.reportPattern=sonar-reports/coverage-swift*.xml - -# OCLint report generated by run-sonar.sh is stored in sonar-reports/oclint.xml -# Change it only if you generate the file on your own -# sonar.swift.swiftlint.report=sonar-reports/*swiftlint.txt - -# Change it only if you generate the file on your own -# sonar.swift.tailor.report=sonar-reports/*tailor.txt - -# Paths to exclude from coverage report (surefire, 3rd party libraries etc.) -# sonar.swift.excludedPathsFromCoverage=pattern1,pattern2 -sonar.swift.excludedPathsFromCoverage=.*Tests.* - -# Ability to skip tests (such as UI Tests running long time) -# Example = sonar.swift.skipTests=UITests - -########################## -# Tailor configuration # -########################## -# Tailor configuration -# -l,--max-line-length=<0-999> maximum Line length (in characters) -# --list-files display Swift source files to be analyzed -# --max-class-length=<0-999> maximum Class length (in lines) -# --max-closure-length=<0-999> maximum Closure length (in lines) -# --max-file-length=<0-999> maximum File length (in lines) -# --max-function-length=<0-999> maximum Function length (in lines) -# --max-name-length=<0-999> maximum Identifier name length (in characters) -# --max-severity= maximum severity -# --max-struct-length=<0-999> maximum Struct length (in lines) -# --min-name-length=<1-999> minimum Identifier name length (in characters) -sonar.swift.tailor.config=--no-color --max-line-length=100 --max-file-length=500 --max-name-length=40 --max-name-length=40 --min-name-length=4