Merge branch 'master' into static/multi_module_static

# Conflicts:
#	gradle/commonStaticAnalysis.gradle
This commit is contained in:
alex 2020-02-07 17:53:20 +03:00
commit 89a89e38c4
17 changed files with 349 additions and 12 deletions

20
android_hooks/pre-push Executable file
View File

@ -0,0 +1,20 @@
#!/bin/sh
echo "Pre push static analysis"
output=$(./gradlew staticAnalysis --daemon 2>&1)
status=$?
if [ "$status" = 0 ] ; then
exit 0
else
build_date=$(date +'%Y-%m-%d_%T')
file_name="build_$build_date"
folder_name="./build_log/"
file_path="${folder_name}${file_name}"
mkdir -p "$folder_name"
echo "$output" >> "$file_path"
echo "Pre push static failed. Full log path: ${file_path}"
exit 1
fi

View File

@ -7,7 +7,7 @@ configurations {
}
dependencies {
apigenerator 'ru.touchin:api-generator:1.2.9'
apigenerator 'ru.touchin:api-generator:1.4.0-beta1'
}
android.applicationVariants.all { variant ->

View File

@ -7,7 +7,7 @@ configurations {
}
dependencies {
apigeneratorKotlinServer 'ru.touchin:api-generator:1.3.2'
apigeneratorKotlinServer 'ru.touchin:api-generator:1.4.0-alpha1'
}
task generateApiModelsKotlinServer doLast {

View File

@ -0,0 +1,12 @@
android {
defaultConfig {
setProperty(
"archivesBaseName",
"${project.android.defaultConfig.applicationId}" +
"-" +
"${project.android.defaultConfig.versionName}" +
"-" +
"${project.android.defaultConfig.versionCode}"
)
}
}

View File

@ -0,0 +1,7 @@
task installGitHooks(type: Copy) {
from new File(buildScriptsDir, 'android_hooks')
into { new File(rootProject.rootDir, '.git/hooks') }
fileMode 0777
}
tasks.getByPath(':app:preBuild').dependsOn installGitHooks

View File

@ -267,5 +267,6 @@
<issue id="ViewHolder" severity="ignore"/>
<issue id="WebViewLayout" severity="ignore"/>
<issue id="TypographyEllipsis" severity="ignore"/>
<issue id="PermissionImpliesUnsupportedChromeOsHardware" severity="ignore"/>
</lint>

View File

@ -6,3 +6,4 @@
-include rules/crashlytics.pro
-include rules/glide.pro
-include rules/kaspersky.pro
-include rules/appsflyer.pro

View File

@ -0,0 +1,5 @@
-dontwarn com.google.android.gms.iid.**
-keep class com.appsflyer.** { *; }
-dontwarn android.app.job.JobParameters
-dontwarn com.android.installreferrer.**
-dontwarn com.appsflyer.**

View File

@ -60,6 +60,7 @@ excluded:
- Carthage
- Pods
- Generated
- Localization
line_length: 128

6
xcode/UnusedConfig.yml Normal file
View File

@ -0,0 +1,6 @@
excluded-resources:
- Generated/
- Resources/Localization
- Carthage/
- Pods/
- FormattingService.swift

View File

@ -1,12 +1,29 @@
file_name=$1
file_link=$2
folder=$3
flag_of_delete=$4
folder="Downloads"
file_path="./${folder}/${file_name}"
readonly key_of_delete="--remove-cached"
readonly default_folder="./Downloads"
if [[ ${folder} = ${key_of_delete} ]]; then
folder="${default_folder}"
flag_of_delete="${key_of_delete}"
fi
if ! [ -n "$folder" ]; then
folder="${default_folder}"
fi
file_path="${folder}/${file_name}"
if [[ ${flag_of_delete} = ${key_of_delete} ]]; then
rm ${file_path}
fi
# make folder if not exist
if ! [ -e ${folder} ]; then
mkdir ${folder}
mkdir -p ${folder}
fi
# download file if not downloaded

View File

@ -1,5 +1,5 @@
<?php
$PROJECT_NAME = $argv[1];
$PRODUCT_NAME = $argv[1];
$COMMON_STRINGS_PATH = $argv[2];
function createFolder($path) {
@ -8,7 +8,7 @@
}
}
$localization = './'.$PROJECT_NAME.'/Resources/Localization/';
$localization = './'.$PRODUCT_NAME.'/Resources/Localization/';
$baseFile = file_get_contents(array_pop(glob($COMMON_STRINGS_PATH.'/default*.json')));
$baseJson = json_decode($baseFile, true);
@ -38,7 +38,7 @@
'// swiftlint:disable line_length'.PHP_EOL.
'// swiftlint:disable file_length'.PHP_EOL.
'// swiftlint:disable identifier_name'.PHP_EOL.PHP_EOL.
'extension String {'.PHP_EOL;
'public extension String {'.PHP_EOL;
foreach ($json as $key=>$value) {
$value_without_linefeed = preg_replace("/\r|\n/", " ", $value);
$ios_swift_strings .= "\t/// ".$value_without_linefeed."\n\t".'static let '.preg_replace_callback('/_(.?)/', function ($m) { return strtoupper($m[1]); }, $key).' = NSLocalizedString("'.$key.'", comment: "")'."\n".PHP_EOL;

View File

@ -0,0 +1,189 @@
# source: https://github.com/PaulTaykalo/swift-scripts/blob/master/unused.rb
#!/usr/bin/ruby
#encoding: utf-8
require 'yaml'
require 'optparse'
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
class Item
def initialize(file, line, at)
@file = file
@line = line
@at = at + 1
if match = line.match(/(func|let|var|class|enum|struct|protocol)\s+(\w+)/)
@type = match.captures[0]
@name = match.captures[1]
end
end
def modifiers
return @modifiers if @modifiers
@modifiers = []
if match = @line.match(/(.*?)#{@type}/)
@modifiers = match.captures[0].split(" ")
end
return @modifiers
end
def name
@name
end
def file
@file
end
def to_s
serialize
end
def to_str
serialize
end
def full_file_path
Dir.pwd + '/' + @file
end
def serialize
"#{@file} has unused \"#{@type.to_s} #{@name.to_s}\""
end
def to_xcode
"#{full_file_path}:#{@at}:0: warning: #{@type.to_s} #{@name.to_s} is unused"
end
end
class Unused
def find
items = []
unused_warnings = []
regexps = parse_arguments
all_files = Dir.glob("**/*.swift").reject do |path|
File.directory?(path)
end
all_files.each { |my_text_file|
file_items = grab_items(my_text_file)
file_items = filter_items(file_items)
non_private_items, private_items = file_items.partition { |f| !f.modifiers.include?("private") && !f.modifiers.include?("fileprivate") }
items += non_private_items
# Usage within the file
if private_items.length > 0
unused_warnings += find_usages_in_files([my_text_file], [], private_items, regexps)
end
}
xibs = Dir.glob("**/*.xib")
storyboards = Dir.glob("**/*.storyboard")
unused_warnings += find_usages_in_files(all_files, xibs + storyboards, items, regexps)
if unused_warnings.length > 0
# show warning
puts "Unused Code Warning!: warning: Total Count #{unused_warnings.length}"
puts "#{unused_warnings.map { |e| e.to_xcode}.join("\n")}"
# write log
File.open("UnusedLog.txt", "w") do |file|
file.write("Unused code warnings count: #{unused_warnings.length}\n\n")
file.write("#{unused_warnings.map { |e| e.serialize }.join("\n")}")
end
end
end
def parse_arguments
resources = []
options = {}
OptionParser.new do |opts|
options[:exclude] = []
opts.on("-c", "--config=FileName") { |c| options[:config] = c }
opts.on("-i", "--exclude [a, b, c]", Array) { |i| options[:exclude] += i if !i.nil? }
end.parse!
# find --config file
if !options[:config].nil?
fileName = options[:config]
resources += YAML.load_file(fileName).fetch("excluded-resources")
elsif
puts "---------\n Warning: Config file is not provided \n---------"
end
# find --exclude files
if !options[:exclude].nil?
resources += options[:exclude]
end
# create and return Regexp
resources.map { |r| Regexp.new(r) }
end
def grab_items(file)
lines = File.readlines(file).map {|line| line.gsub(/^\s*\/\/.*/, "") }
items = lines.each_with_index.select { |line, i| line[/(func|let|var|class|enum|struct|protocol)\s+\w+/] }.map { |line, i| Item.new(file, line, i)}
end
def filter_items(items)
items.select { |f|
!f.name.start_with?("test") && !f.modifiers.include?("@IBAction") && !f.modifiers.include?("override") && !f.modifiers.include?("@objc") && !f.modifiers.include?("@IBInspectable")
}
end
# remove files, that maches excluded Regexps array
def ignore_files_with_regexps(files, regexps)
files.select { |f| regexps.all? { |r| r.match(f.file).nil? } }
end
def find_usages_in_files(files, xibs, items_in, regexps)
items = items_in
usages = items.map { |f| 0 }
files.each { |file|
lines = File.readlines(file).map {|line| line.gsub(/^[^\/]*\/\/.*/, "") }
words = lines.join("\n").split(/\W+/)
words_arrray = words.group_by { |w| w }.map { |w, ws| [w, ws.length] }.flatten
wf = Hash[*words_arrray]
items.each_with_index { |f, i|
usages[i] += (wf[f.name] || 0)
}
# Remove all items which has usage 2+
indexes = usages.each_with_index.select { |u, i| u >= 2 }.map { |f, i| i }
# reduce usage array if we found some functions already
indexes.reverse.each { |i| usages.delete_at(i) && items.delete_at(i) }
}
xibs.each { |xib|
lines = File.readlines(xib).map {|line| line.gsub(/^\s*\/\/.*/, "") }
full_xml = lines.join(" ")
classes = full_xml.scan(/(class|customClass)="([^"]+)"/).map { |cd| cd[1] }
classes_array = classes.group_by { |w| w }.map { |w, ws| [w, ws.length] }.flatten
wf = Hash[*classes_array]
items.each_with_index { |f, i|
usages[i] += (wf[f.name] || 0)
}
# Remove all items which has usage 2+
indexes = usages.each_with_index.select { |u, i| u >= 2 }.map { |f, i| i }
# reduce usage array if we found some functions already
indexes.reverse.each { |i| usages.delete_at(i) && items.delete_at(i) }
}
items = ignore_files_with_regexps(items, regexps)
end
end
Unused.new.find

View File

@ -6,4 +6,4 @@ link="https://dl.bintray.com/touchin/touchin-tools/ru/touchin/api-generator/${VE
. build-scripts/xcode/aux_scripts/download_file.sh ${FILE_NAME} ${link}
# execute api generator
java -jar "Downloads/${FILE_NAME}" generate-client-code --output-language SWIFT --specification-path common/api --output-path ${PROJECT_NAME}/Generated --single-file true
java -jar "Downloads/${FILE_NAME}" generate-client-code --output-language SWIFT --specification-path common/api --output-path ${PRODUCT_NAME}/Generated --single-file true

View File

@ -1,4 +1,4 @@
LOCALIZATION_PATH="${PROJECT_NAME}/Resources/Localization"
LOCALIZATION_PATH="${PRODUCT_NAME}/Resources/Localization"
#first argument set strings folder path
STRINGS_FOLDER=${1:-"common/strings"}
@ -13,4 +13,4 @@ if ! [ -e "${PROJECT_DIR}/${STRINGS_FOLDER}" ]; then
fi
#second argument set strings script path
php ${2:-build-scripts/xcode/aux_scripts/import_strings.php} ${PROJECT_NAME} ${STRINGS_FOLDER}
php ${2:-build-scripts/xcode/aux_scripts/import_strings.php} ${PRODUCT_NAME} ${STRINGS_FOLDER}

View File

@ -0,0 +1,4 @@
arguments=("$@")
ignored_files=$(IFS=, ; echo "${arguments[*]}")
ruby ${PROJECT_DIR}/build-scripts/xcode/build_phases/Unused.rb --config ${PROJECT_DIR}/build-scripts/xcode/UnusedConfig.yml --exclude ${ignored_files}

View File

@ -9,6 +9,10 @@ private_lane :installDependencies do |options|
sh("rm -rf #{podsReposPath}")
end
if File.exists? "../Gemfile"
bundle_install(path: "../.gem")
end
if File.exists? "../Cartfile"
begin
carthage(command: "bootstrap", platform: "iOS")
@ -20,7 +24,7 @@ private_lane :installDependencies do |options|
end
cocoapods(
repo_update: false
repo_update: true
)
end
@ -196,6 +200,76 @@ private_lane :openKeychain do |options|
end
end
lane :ManuallyUpdateCodeSigning do |options|
# based on this article https://medium.com/@jonathancardoso/using-fastlane-match-with-existing-certificates-without-revoking-them-a325be69dac6
require 'fastlane_core'
require 'match'
conf = FastlaneCore::Configuration.create(Match::Options.available_options, {})
conf.load_configuration_file("Matchfile")
git_url = conf.config_file_options[:git_url]
shallow_clone = false
branch = 'fastlane_certificates'
storage_conf = lambda do
new_storage = Match::Storage.for_mode('git', { git_url: git_url, shallow_clone: shallow_clone, git_branch: branch, clone_branch_directly: false})
new_storage.download
return new_storage
end
encryption_conf_for_storage = lambda do |stor|
new_encryption = Match::Encryption.for_storage_mode('git', { git_url: git_url, working_directory: stor.working_directory})
new_encryption.decrypt_files
return new_encryption
end
get_all_files = lambda do |stor|
Dir[File.join(stor.working_directory, "**", "*.{cer,p12,mobileprovision}")]
end
storage = storage_conf.call
encryption = encryption_conf_for_storage.call(storage)
old_files = get_all_files.call(storage)
sh("open #{storage.working_directory}")
# we are not using prompt() since it requires non-empty input which is not a case for Enter (\n)
puts "Enter any key when you're done"
STDIN.gets
encryption.encrypt_files
files_to_commit = get_all_files.call(storage)
old_directory = storage.working_directory
storage.save_changes!(files_to_commit: files_to_commit)
# need to check, because saving changes with delete is another function (update repo if needed)
files_diff = old_files - files_to_commit
# match can not work with both save/delete functionality `You can't provide both files_to_delete and files_to_commit right now`
# to avoid this we use storage twice if needed
if files_diff.length > 0
storage = storage_conf.call
encryption = encryption_conf_for_storage.call(storage)
files_to_delete = files_diff.map do |file|
old_file = file
old_file.slice! old_directory
new_file = File.join(storage.working_directory, old_file)
File.delete(new_file) if File.exist?(new_file)
file = new_file
end
encryption.encrypt_files
storage.save_changes!(files_to_delete: files_to_delete)
end
end
def get_keychain_options(options)
keychain_name = options[:keychain_name]
keychain_password = options[:keychain_password]