Compare commits

..

2 Commits

Author SHA1 Message Date
Vlad a9382c5329 Merge branch 'master' into feature/strong_self
# Conflicts:
#	xcode/.swiftlint.yml
2020-09-18 13:21:13 +03:00
Vlad a264c319c2 Add strong self rule 2020-09-18 13:19:47 +03:00
55 changed files with 887 additions and 2220 deletions

3
.gitignore vendored
View File

@ -1,3 +0,0 @@
# macOS
.DS_Store

View File

@ -1,26 +1 @@
# BuildScripts # BuildScripts
## Настройки форматирования
Позволяют настроить одинаковое форматирования кода в Android Studio у всех, кто работает на проекте.
Настройки соответствуют
[Правилам оформления Kotlin кода](https://styleguide.docs.touchin.ru/Coding/KotlinCodestyle.html)
Есть два варианта использования: подключить к проекту или импортировать схему в Android Studio.
### Как подключить к проекту:
1. Скопировать директорию [`codeStyles`](./codeStyles) в директорию проекта `.idea`
2. Добавить в файл `.gitignore` строку `!.idea/codeStyles`
3. Перезапустить Android Studio, чтобы настройки применились
При таком варианте настройки будут применены у всех, кто работает на проекте.
И только для одного конкретного проекта.
### Как импортировать схему в Android Studio:
1. Скачать схему [`codeStyles/Project.xml`](./codeStyles/Project.xml)
2. В Android Studio перейти в `File` > `Settings` > `Editor` > `Code Style`
3. Нажать на шестеренку справа от выпадающего списка схем и выбрать `Import Scheme`
4. В открывшемся окне указать путь до сохраненной схемы и нажать `ОК`
5. В открывшемся окне ввести название новой схемы и нажать `ОК`

8
code.style.schemes Normal file
View File

@ -0,0 +1,8 @@
<application>
<component name="CodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value />
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="TouchInstinct" />
</component>
</application>

5
code.style.schemes.xml Normal file
View File

@ -0,0 +1,5 @@
<application>
<component name="CodeStyleSchemeSettings">
<option name="CURRENT_SCHEME_NAME" value="TouchInstinct" />
</component>
</application>

View File

@ -1,249 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="120" />
<option name="SOFT_MARGINS" value="100" />
<AndroidXmlCodeStyleSettings>
<option name="LAYOUT_SETTINGS">
<value>
<option name="INSERT_LINE_BREAK_BEFORE_NAMESPACE_DECLARATION" value="true" />
</value>
</option>
<option name="MANIFEST_SETTINGS">
<value>
<option name="INSERT_LINE_BREAK_BEFORE_NAMESPACE_DECLARATION" value="true" />
</value>
</option>
<option name="OTHER_SETTINGS">
<value>
<option name="INSERT_LINE_BREAK_BEFORE_NAMESPACE_DECLARATION" value="true" />
</value>
</option>
</AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings>
<option name="GENERATE_FINAL_LOCALS" value="true" />
<option name="GENERATE_FINAL_PARAMETERS" value="true" />
<option name="DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION" value="true" />
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" />
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" static="false" withSubpackages="true" />
<emptyLine />
<package name="com" static="false" withSubpackages="true" />
<emptyLine />
<package name="junit" static="false" withSubpackages="true" />
<emptyLine />
<package name="net" static="false" withSubpackages="true" />
<emptyLine />
<package name="org" static="false" withSubpackages="true" />
<emptyLine />
<package name="java" static="false" withSubpackages="true" />
<emptyLine />
<package name="javax" static="false" withSubpackages="true" />
<emptyLine />
<package name="" static="false" withSubpackages="true" />
<emptyLine />
<package name="" static="true" withSubpackages="true" />
<emptyLine />
</value>
</option>
<option name="JD_P_AT_EMPTY_LINES" value="false" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="true" />
<option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="true" />
<option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="true" />
<option name="CONTINUATION_INDENT_FOR_CHAINED_CALLS" value="true" />
<option name="CONTINUATION_INDENT_IN_ELVIS" value="true" />
<option name="ALLOW_TRAILING_COMMA" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair header="h" source="cpp" />
<pair header="h" source="c" />
</extensions>
</Objective-C-extensions>
<XML>
<option name="XML_KEEP_BLANK_LINES" value="1" />
</XML>
<codeStyleSettings language="JAVA">
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="RESOURCE_LIST_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="THROWS_LIST_WRAP" value="1" />
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="WRAP_LONG_LINES" value="true" />
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
<option name="ENUM_CONSTANTS_WRAP" value="5" />
</codeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
<option name="ENUM_CONSTANTS_WRAP" value="5" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@ -0,0 +1,254 @@
<code_scheme name="TouchInstinct">
<option name="GENERATE_FINAL_LOCALS" value="true" />
<option name="GENERATE_FINAL_PARAMETERS" value="true" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
</value>
</option>
<option name="RIGHT_MARGIN" value="150" />
<option name="JD_P_AT_EMPTY_LINES" value="false" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings>
<option name="DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION" value="true" />
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" />
</JavaCodeStyleSettings>
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" />
<pair source="c" header="h" />
</extensions>
</Objective-C-extensions>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="150" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="RESOURCE_LIST_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="THROWS_LIST_WRAP" value="1" />
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="WRAP_LONG_LINES" value="true" />
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
<option name="ENUM_CONSTANTS_WRAP" value="2" />
</codeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>

View File

@ -7,7 +7,7 @@ plugins {
// The kotlin-dsl plugin requires a repository to be declared // The kotlin-dsl plugin requires a repository to be declared
repositories { repositories {
mavenCentral() jcenter()
google() google()
} }
@ -36,10 +36,6 @@ gradlePlugin {
id = "api-generator-android" id = "api-generator-android"
implementationClass = "apigen.ApiGeneratorAndroidPlugin" implementationClass = "apigen.ApiGeneratorAndroidPlugin"
} }
create("swagger-generator-android") {
id = "swagger-generator-android"
implementationClass = "apigen.SwaggerApiGeneratorAndroidPlugin"
}
create("api-generator-backend") { create("api-generator-backend") {
id = "api-generator-backend" id = "api-generator-backend"
implementationClass = "apigen.ApiGeneratorBackendPlugin" implementationClass = "apigen.ApiGeneratorBackendPlugin"

View File

@ -12,14 +12,14 @@ abstract class ApiGeneratorPlugin : Plugin<Project> {
companion object { companion object {
const val API_GENERATOR_CONFIG = "apiGenerator" const val API_GENERATOR_CONFIG = "apiGenerator"
const val API_GENERATOR_EXT_NAME = "apiGenerator" const val API_GENERATOR_EXT_NAME = "apiGenerator"
const val API_GENERATOR_DEFAULT_VERSION = "1.4.0-beta10" const val API_GENERATOR_DEFAULT_VERSION = "1.4.0-beta5"
} }
override fun apply(target: Project) { override fun apply(target: Project) {
with(target) { with(target) {
repositories { repositories {
maven { maven {
url = uri("https://maven.dev.touchin.ru") url = uri("https://dl.bintray.com/touchin/touchin-tools")
metadataSources { metadataSources {
artifact() artifact()
} }

View File

@ -1,91 +0,0 @@
package apigen
import org.gradle.api.Action
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.repositories
class SwaggerApiGeneratorAndroidPlugin : Plugin<Project> {
private companion object {
const val GENERATOR_CONFIG = "swaggerCodegen"
const val GENERATOR_VERSION = "3.0.34"
const val TI_GENERATOR_CONFIG = "TIKotlin-swagger-codegen"
const val TI_GENERATOR_VERSION = "1.0.0"
const val GENERATOR_EXT_NAME = "swaggerApiGenerator"
const val MAVEN_URL = "https://maven.dev.touchin.ru"
}
override fun apply(target: Project) {
with(target) {
repositories {
maven {
url = uri(MAVEN_URL)
metadataSources {
artifact()
}
}
}
configurations.create(GENERATOR_CONFIG)
configurations.create(TI_GENERATOR_CONFIG)
dependencies {
add(TI_GENERATOR_CONFIG, "ru.touchin:TIKotlin-swagger-codegen:$TI_GENERATOR_VERSION")
add(GENERATOR_CONFIG, "io.swagger.codegen.v3:swagger-codegen-cli:$GENERATOR_VERSION")
}
extensions.create<SwaggerApiGeneratorExtension>(GENERATOR_EXT_NAME)
val apiGenTask = createSwaggerApiGeneratorTask()
gradle.projectsEvaluated {
tasks.getByName("preBuild").dependsOn(apiGenTask)
}
}
}
protected fun Project.getExtension(): SwaggerApiGeneratorExtension = extensions.getByName(GENERATOR_EXT_NAME) as SwaggerApiGeneratorExtension
private fun Project.createSwaggerApiGeneratorTask(): Task = tasks.create(GENERATOR_CONFIG).doLast {
val extension = getExtension()
val taskWorkingDir = extension.taskWorkingDir ?: throw IllegalStateException("Configure taskWorkingDir for swagger generator plugin")
val apiSchemesFilePath = extension.apiSchemesFilePath ?: throw IllegalStateException("Configure sourceFilePath for swagger generator plugin")
val outputDir = extension.outputDir ?: throw IllegalStateException("Configure outputDir for swagger generator plugin")
val projectName = extension.projectName ?: throw IllegalStateException("Configure projectName for swagger generator plugin")
javaexec {
workingDir = file(taskWorkingDir)
classpath = files(configurations.getByName(GENERATOR_CONFIG).asPath,
configurations.getByName(TI_GENERATOR_CONFIG).asPath)
main = "io.swagger.codegen.v3.cli.SwaggerCodegen"
args = listOfNotNull(
"generate",
"-i",
apiSchemesFilePath,
"-l",
"TIKotlinCodegen",
"-o",
outputDir,
"--additional-properties",
"projectName=$projectName"
)
}
}
}
open class SwaggerApiGeneratorExtension(
var taskWorkingDir: String? = null,
var apiSchemesFilePath: String? = null,
var outputDir: String? = null,
var projectName: String? = null
)
fun Project.swaggerApiGenerator(configure: Action<SwaggerApiGeneratorExtension>): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("swaggerApiGenerator", configure)

View File

@ -33,8 +33,8 @@ class AndroidLinter : Linter {
.flatten() .flatten()
override fun setupForProject(project: Project, extension: StaticAnalysisExtension) { override fun setupForProject(project: Project, extension: StaticAnalysisExtension) {
project.beforeEvaluate { project.gradle.projectsEvaluated {
subprojects project.subprojects
.mapNotNull { it.extensions.findByType<AppExtension>() } .mapNotNull { it.extensions.findByType<AppExtension>() }
.first() .first()
.lintOptions.apply { .lintOptions.apply {
@ -45,8 +45,8 @@ class AndroidLinter : Linter {
htmlReport = false htmlReport = false
isCheckDependencies = true isCheckDependencies = true
disable("MissingConstraints", "VectorRaster") disable("MissingConstraints", "VectorRaster")
xmlOutput = getLintReportFile() xmlOutput = project.getLintReportFile()
lintConfig = file("${extension.buildScriptDir}/static_analysis_configs/lint.xml") lintConfig = project.file("${extension.buildScriptDir}/static_analysis_configs/lint.xml")
} }
} }
} }

View File

@ -34,18 +34,15 @@ class CpdLinter : Linter {
} }
override fun setupForProject(project: Project, extension: StaticAnalysisExtension) { override fun setupForProject(project: Project, extension: StaticAnalysisExtension) {
project.afterEvaluate { project.extensions.findByType<CpdExtension>()!!.apply {
extensions.findByType<CpdExtension>()!!.apply { isSkipLexicalErrors = true
isSkipLexicalErrors = true language = "kotlin"
language = "kotlin" minimumTokenCount = 60
minimumTokenCount = 60 }
} project.tasks.withType<Cpd> {
tasks.withType<Cpd> { reports.xml.destination = project.getCpdReportFile()
reports.xml.required.set(true) ignoreFailures = true
reports.xml.destination = getCpdReportFile() source = project.getSources(extension.excludes)
ignoreFailures = true
source = getSources(extension.excludes)
}
} }
} }

View File

@ -32,27 +32,27 @@ class DetektLinter : Linter {
.flatten() .flatten()
override fun setupForProject(project: Project, extension: StaticAnalysisExtension) { override fun setupForProject(project: Project, extension: StaticAnalysisExtension) {
project.afterEvaluate { project
tasks.withType(Detekt::class.java) { .tasks
exclude("**/test/**") .withType(Detekt::class.java) {
exclude("resources/") exclude("**/test/**")
exclude("build/") exclude("resources/")
exclude("tmp/") exclude("build/")
jvmTarget = "1.8" exclude("tmp/")
jvmTarget = "1.8"
config.setFrom(files("${extension.buildScriptDir!!}/static_analysis_configs/detekt-config.yml")) config.setFrom(project.files("${extension.buildScriptDir!!}/static_analysis_configs/detekt-config.yml"))
reports { reports {
txt.enabled = false txt.enabled = false
html.enabled = false html.enabled = false
xml { xml {
enabled = true enabled = true
destination = getDetektReportFile() destination = project.getDetektReportFile()
}
} }
}
source = getSources(extension.excludes) source = project.getSources(extension.excludes)
} }
}
} }
override fun getTaskNames(project: Project, buildType: String?): List<String> = listOf(":detekt") override fun getTaskNames(project: Project, buildType: String?): List<String> = listOf(":detekt")

View File

@ -28,6 +28,8 @@ class StaticAnalysisAndroidPlugin : StaticAnalysisPlugin() {
) )
} }
} }
} }
} }
} }

View File

@ -26,7 +26,9 @@ abstract class StaticAnalysisPlugin : Plugin<Project> {
val linters = createLinters() val linters = createLinters()
linters.forEach { it.setupForProject(target, extensions.getByType()) } afterEvaluate {
linters.forEach { it.setupForProject(target, extensions.getByType()) }
}
gradle.projectsEvaluated { gradle.projectsEvaluated {
createStaticAnalysisTasks(target, linters) createStaticAnalysisTasks(target, linters)

View File

@ -1,71 +1,31 @@
#!/bin/sh #!/bin/sh
# Description:
# Creates archive with source code of multiple repositories.
#
# Parameters:
# $1 - github repository name without suffix (project name).
# $2, $3, ..., $n - repository suffixes (platforms).
#
# Optional environment variables:
# GIT_BRANCH - branch to use. Default - master.
#
# Example of usage:
# export_src.sh TestProject ios android backend
# GIT_BRANCH="develop" ./export_src.sh TestProject ios web
#
if [ -z "${GIT_BRANCH}" ]; then
GIT_BRANCH="master"
fi
LAST_COMMIT_DATE=""
PROJECT_NAME=$1 PROJECT_NAME=$1
SRC_FOLDER_NAME="${PROJECT_NAME}-src" SRC_FOLDER_NAME=${PROJECT_NAME}-src-$(date +%F)
SRC_DIR="./${SRC_FOLDER_NAME}" SRC_DIR=./${SRC_FOLDER_NAME}
COMMAND_LINE_ARGUMENTS=$@ COMMAND_LINE_ARGUMENTS=$@
clone_platform() { clone_platform() {
PROJECT_NAME=$1 PROJECT_DIR=$1
PLATFORM=$2 PLATFORM=$2
if git clone --recurse-submodules -j8 "ssh://git@git.ti:7999/touchinstinct/${PROJECT_NAME}-${PLATFORM}.git" --branch "${GIT_BRANCH}"; then git clone --recurse-submodules -j8 git@github.com:TouchInstinct/${PROJECT_DIR}-${PLATFORM}.git --branch master
cd ${PROJECT_NAME}-${PLATFORM}
COMMIT_DATE=`git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d'`
if [[ $LAST_COMMIT_DATE < $COMMIT_DATE ]]; then
LAST_COMMIT_DATE="${COMMIT_DATE}"
fi
cd ..
else
exit 1
fi
} }
mkdir -p "${SRC_DIR}" mkdir -p ${SRC_DIR}
cd "${SRC_DIR}" cd ${SRC_DIR}
for argument in ${COMMAND_LINE_ARGUMENTS} for argument in ${COMMAND_LINE_ARGUMENTS}
do do
if [ $argument != $PROJECT_NAME ]; then if [ $argument != $PROJECT_NAME ]
then
platform=${argument} # all arguments after project name treated as platforms platform=${argument} # all arguments after project name treated as platforms
clone_platform ${PROJECT_NAME} ${platform} clone_platform ${PROJECT_NAME} ${platform}
fi fi
done done
ERR_PATHS=$(find . -name "*[<>:\\|?*]*" | xargs -I %s echo "- %s")
if [ "$ERR_PATHS" ]; then
echo "Export aborted! Invalid characters found in file or directories name(s):\n$ERR_PATHS"
exit 1
fi
if [ -z "${EXPORT_DATE}" ]; then
EXPORT_DATE="${LAST_COMMIT_DATE}"
fi
find . -name ".git*" -print0 | xargs -0 rm -rf find . -name ".git*" -print0 | xargs -0 rm -rf
zip -r -q "${SRC_FOLDER_NAME}-${EXPORT_DATE}".zip . zip -r ${SRC_FOLDER_NAME}.zip .
open . open .

View File

@ -387,6 +387,9 @@ style:
UnusedPrivateMember: UnusedPrivateMember:
active: true active: true
allowedNames: "(_|ignored|expected|serialVersionUID)" allowedNames: "(_|ignored|expected|serialVersionUID)"
UseDataClass:
active: true
excludeAnnotatedClasses: ""
UtilityClassWithPublicConstructor: UtilityClassWithPublicConstructor:
active: false active: false
VarCouldBeVal: VarCouldBeVal:

View File

@ -15,6 +15,7 @@ opt_in_rules:
# idiomatic # idiomatic
- legacy_random
- legacy_multiple - legacy_multiple
- pattern_matching_keywords - pattern_matching_keywords
- redundant_nil_coalescing - redundant_nil_coalescing
@ -31,12 +32,6 @@ opt_in_rules:
- fatal_error_message - fatal_error_message
- extension_access_modifier - extension_access_modifier
- explicit_init - explicit_init
- fallthrough
- unavailable_function
- prefer_zero_over_explicit_init
- discouraged_assert
- discouraged_none_name
- shorthand_optional_binding
# style # style
@ -58,46 +53,27 @@ opt_in_rules:
- closure_spacing - closure_spacing
- closure_end_indentation - closure_end_indentation
- prefer_self_type_over_type_of_self - prefer_self_type_over_type_of_self
- closure_parameter_position
- comma_inheritance
- self_binding
- prefer_self_in_static_references
- direct_return
- period_spacing
# lint # lint
- private_action - private_action
- private_outlet - private_outlet
- prohibited_super_call - prohibited_super_call
- unused_import
- unused_declaration
- identical_operands - identical_operands
- overridden_super_call - overridden_super_call
- unowned_variable_capture - unowned_variable_capture
- strong_iboutlet
- lower_acl_than_parent
- comment_spacing
- ibinspectable_in_extension
- private_subject
- unhandled_throwing_task
# metrics # metrics
- enum_case_associated_values_count - enum_case_associated_values_count
analyzer_rules:
- capture_variable
- typesafe_array_init
- unused_declaration
- unused_import
excluded: excluded:
- Carthage - Carthage
- Pods - Pods
- Generated - Generated
- "**/Generated" - Localization
- "**/Resources"
- ".gem"
- "**/*.app"
line_length: line_length:
warning: 128 warning: 128
@ -129,30 +105,40 @@ identifier_name:
- id - id
- ok - ok
- URL - URL
- qr
- x - x
- y - y
- z - z
warning_threshold: 1 warning_threshold: 1
allow_zero_lintable_files: true
custom_rules: custom_rules:
# General # General
unsecure_logging: uiwebview_disabled:
name: "Unsecure logging" included: ".*.swift"
regex: '\s(print|debugPrint|NSLog)\(' name: "UIWebView Usage Disabled"
message: "Please use os_log or remove this debug statement" regex: 'UIWebView'
message: "Do not use UIWebView. Use WKWebView Instead. https://developer.apple.com/reference/uikit/uiwebview"
severity: error
native_print:
name: "print -> DDLog"
regex: '(print|NSLog)\('
message: "Please use CocoaLumberjack instead `print` and `NSlog`"
severity: error
uiedge_insets_zero:
name: "UIEdgeInsets .zero"
regex: '\(top: 0, left: 0, bottom: 0, right: 0\)'
message: "Please use short init `.zero`."
severity: error
let_variable:
name: "Let Variable"
regex: 'var\s\w*(:|(\s=))\sVariable'
message: "Please make variable using `let`."
severity: error severity: error
excluded_match_kinds:
- comment
- comment.mark
- comment.url
- doccomment
- doccomment.field
marks_style: marks_style:
name: "Marks" name: "Marks"
@ -173,12 +159,19 @@ custom_rules:
message: "Type definition not needed" message: "Type definition not needed"
severity: error severity: error
unsafe_unowned: unowned:
name: "Unsafe unowned usage" name: "Unowned"
regex: 'unowned' regex: 'unowned'
message: "Please use `weak` instead." message: "Please use `weak` instead. "
severity: error severity: error
continue_keyword:
name: "Continue"
regex: 'continue'
message: "Don't use continue instruction"
severity: error
match_kinds: keyword
cyrillic_strings: cyrillic_strings:
name: "Cyrillic strings" name: "Cyrillic strings"
regex: '[а-яА-Я]+' regex: '[а-яА-Я]+'
@ -193,12 +186,26 @@ custom_rules:
regex: '(?!\n)[^ \n]+ {2,}.+' regex: '(?!\n)[^ \n]+ {2,}.+'
message: "Remove excess empty spaces" message: "Remove excess empty spaces"
severity: warning severity: warning
excluded_match_kinds: match_kinds:
- comment - argument
- comment.mark - attribute.builtin
- comment.url - attribute.id
- doccomment - buildconfig.id
- doccomment.field - buildconfig.keyword
- identifier
- keyword
- number
- objectliteral
- parameter
- placeholder
# - string # all except string literals
# - comment # and comments
# - comment.mark
# - comment.url
# - doccomment
# - doccomment.field
- string_interpolation_anchor
- typeidentifier
getter_setter_style: getter_setter_style:
name: "Wrong getter/setter code style" name: "Wrong getter/setter code style"
@ -206,61 +213,55 @@ custom_rules:
match_kinds: match_kinds:
- keyword - keyword
message: "Make a new line break when use getter or setter" message: "Make a new line break when use getter or setter"
severity: warning severity: error
redundant_boolean_condition: redundant_boolean_condition:
name: "Redundant Boolean Condition" name: "Redundant Boolean Condition"
regex: "(== true)|(== false)|(!= true)|(!= false)" regex: "(== true)|(== false)|(!= true)|(!= false)"
message: "Comparing a boolean to true is redundant (use `?? false` for optionals), and `!`-syntax is preferred over comparing to false." message: "Comparing a boolean to true is redundant (use `?? false` for optionals), and `!`-syntax is preferred over comparing to false."
severity: warning severity: error
excluded_match_kinds:
- comment
- comment.mark
- comment.url
- doccomment
- doccomment.field
redundant_ternary_operator: redundant_ternary_operator:
name: "Redundant Ternary Operator" name: "Redundant Ternary Operator"
regex: "(\\? true \\: false)|(\\? false \\: true)" regex: "(\\? true \\: false)|(\\? false \\: true)"
message: "Returning a boolean as true is redundant, and `!`-syntax is preferred over returning as false." message: "Returning a boolean as true is redundant, and `!`-syntax is preferred over returning as false."
severity: warning severity: error
single_line_closure: single_line_closure:
name: "Single line closure" name: "Single line closure"
regex: '\{([^\n\/]*\[[^\]]+\][^\n\/]*)?([^\n\/]*[a-zA-Z]\w*(, \w+)*)? in [^\n\/]+' regex: '\{([^\n\/]*\[[^\]]+\][^\n\/]*)?([^\n\/]*[a-zA-Z]\w*(, \w+)*)? in [^\n\/]+'
message: "Too complex expression for single line closure. Improve readability by making it multiline." message: "Too complex expression for single line closure. Improve readability by making it multiline."
severity: warning severity: error
addSubview_in_cell: addSubview_in_cell:
name: "Usage addSubview in cell" name: "Usage addSubview in cell"
regex: '(extension|class)\s*\w+Cell(:| )(?s).*(self\.|\s{2,})add(Subv|V)iews?\(\w' regex: '(extension|class)\s*\w+Cell(:| )(?s).*(self\.|\s{2,})add(Subv|V)iews?\(\w'
message: "Use сontentView instead of self for addSubview or addSubviews methods in cell." message: "Use сontentView instead of self for addSubview or addSubviews methods in cell."
severity: warning severity: error
redundant_type_annotation_bool:
name: "Redundant type annotation for Bool"
regex: '\s((var|let))\s{1,}\w+ *((: *Bool *=)|((\w| |<|>|:)*= *BehaviorRelay<Bool>\( *value *:)) *((true)|(false))'
message: "Using a type annotation for Bool is redundant."
severity: error
parameter_repetition: parameter_repetition:
name: "Parameter repetition" name: "Parameter repetition"
regex: 'func ((\w+([A-Z]\w+))|(\w+)) *(<[^>]+>)? *\( *(?i)(\3|\4):' regex: 'func ((\w+([A-Z]\w+))|(\w+)) *(<[^>]+>)? *\( *(?i)(\3|\4):'
message: "The parameter name is actually used in the function name. Use _ instead." message: "The parameter name is actually used in the function name. Use _ instead."
severity: warning severity: error
parameter_closure: parameter_closure:
name: "Parameter closure" name: "Parameter closure"
regex: '\w*Closure<[^\r\n\t\f\v]*, Void[^\r\n\t\f\v]*>' regex: '\w*Closure<[^\r\n\t\f\v]*, Void[^\r\n\t\f\v]*>'
message: "Use `ParameterClosure` instead of declaring an explicit return value of `Void`." message: "Use `ParameterClosure` instead of declaring an explicit return value of `Void`."
severity: warning severity: error
strong_self: strong_self:
name: "Strong self" name: "Strong self"
regex: '(if|guard)\s+let\s+self\s+=\s+self' regex: 'guard[\s\S]*= *self[\s\S]*else'
message: "Use a local function instead of capture strong self" message: "Use a local function with `weak self` instead of `strong self`."
severity: warning severity: error
pattern_matching:
name: "Pattern matching"
regex: 'case[^\n\(]+\([^\)]*(let|var)\s'
message: "Use a let|var keyword behind parentheses"
severity: warning
# Rx # Rx

View File

@ -3,8 +3,8 @@ file_link=$2
folder=$3 folder=$3
flag_of_delete=$4 flag_of_delete=$4
key_of_delete="--remove-cached" readonly key_of_delete="--remove-cached"
default_folder="./Downloads" readonly default_folder="./Downloads"
if [[ ${folder} = ${key_of_delete} ]]; then if [[ ${folder} = ${key_of_delete} ]]; then
folder="${default_folder}" folder="${default_folder}"

View File

@ -1,13 +1,14 @@
<?php <?php
$LOCALIZATION_PATH = $argv[1]; $PRODUCT_NAME = $argv[1];
$COMMON_STRINGS_PATH = $argv[2]; $COMMON_STRINGS_PATH = $argv[2];
$BUNDLE = $argv[3];
function createFolder($path) { function createFolder($path) {
if (!file_exists($path)) { if (!file_exists($path)) {
mkdir($path, 0777, true); mkdir($path, 0777, true);
} }
} }
$localization = './'.$PRODUCT_NAME.'/Resources/Localization/';
$baseFile = file_get_contents(array_pop(glob($COMMON_STRINGS_PATH.'/default*.json'))); $baseFile = file_get_contents(array_pop(glob($COMMON_STRINGS_PATH.'/default*.json')));
$baseJson = json_decode($baseFile, true); $baseJson = json_decode($baseFile, true);
@ -30,26 +31,25 @@
} }
$ios_strings = preg_replace('/(\\\\)(u)([0-9a-fA-F]{4})/', '$1U$3', $ios_strings); $ios_strings = preg_replace('/(\\\\)(u)([0-9a-fA-F]{4})/', '$1U$3', $ios_strings);
$lproj = $LOCALIZATION_PATH.$languageName.'.lproj/'; $lproj = $localization.$languageName.'.lproj/';
createFolder($lproj); createFolder($lproj);
file_put_contents($lproj.'Localizable.strings', $ios_strings); file_put_contents($lproj.'Localizable.strings', $ios_strings);
if($isBase) { if($isBase) {
createFolder($LOCALIZATION_PATH.'Base.lproj/'); createFolder($localization.'Base.lproj/');
file_put_contents($LOCALIZATION_PATH.'Base.lproj/Localizable.strings', $ios_strings); file_put_contents($localization.'Base.lproj/Localizable.strings', $ios_strings);
$ios_swift_strings = 'import Foundation'.PHP_EOL.PHP_EOL. $ios_swift_strings = 'import Foundation'.PHP_EOL.PHP_EOL.
'// swiftlint:disable superfluous_disable_command'.PHP_EOL. '// swiftlint:disable superfluous_disable_command'.PHP_EOL.
'// swiftlint:disable line_length'.PHP_EOL. '// swiftlint:disable line_length'.PHP_EOL.
'// swiftlint:disable file_length'.PHP_EOL. '// swiftlint:disable file_length'.PHP_EOL.
'// swiftlint:disable cyrillic_strings'.PHP_EOL.
'// swiftlint:disable identifier_name'.PHP_EOL.PHP_EOL. '// swiftlint:disable identifier_name'.PHP_EOL.PHP_EOL.
'public extension String {'.PHP_EOL; 'public extension String {'.PHP_EOL;
foreach ($json as $key=>$value) { foreach ($json as $key=>$value) {
$value_without_linefeed = preg_replace("/\r|\n/", " ", $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.'", bundle: '.$BUNDLE.', comment: "'.addslashes($value_without_linefeed).'")'."\n".PHP_EOL; $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;
} }
$ios_swift_strings .= '}'.PHP_EOL; $ios_swift_strings .= '}'.PHP_EOL;
file_put_contents($LOCALIZATION_PATH.'String+Localization.swift', $ios_swift_strings); file_put_contents($localization.'String+Localization.swift', $ios_swift_strings);
} }
} }
?> ?>

View File

@ -1,39 +0,0 @@
#!/bin/sh
# Description:
# Add user defined enviroment if programm not found
#
# Parameters:
# $1 - programm
#
# Examples of usage:
# . install_env.sh pmd
#
# When you run Git from the command line, it runs in the environment as set up by your Shell.
# GUI OS X apps, however, have no knowledge about your shell - and the PATH environment can be changed in many different places.
# Export our profile with path by ourselves
function source_home_file {
file="$HOME/$1"
if [[ -f "${file}" ]]; then
if ! source "${file}"; then
export_commands="$(cat "${file}" | grep "^export PATH=")"
while read export_command
do
eval "$export_command"
done <<< "$export_commands"
fi
fi
return 1
}
# Use specific exec due to Xcode has custom value of $PATH
if [ -z "$(which $1)" ]; then
source_home_file ".bash_profile" || source_home_file ".zshrc" || source_home_file ".zprofile" || true
echo "User defined enviroment has been set for ${1}"
fi

View File

@ -1,14 +0,0 @@
# Working environment
brew "rbenv" # ruby + bundler
brew "gettext"
# Code, configs and project generation
brew "php"
brew "python"
brew "xcodegen"
# code quality
brew "pmd"
# CI badge
# brew "imagemagick"

View File

@ -1,9 +0,0 @@
source "https://rubygems.org"
gem "cocoapods"
gem "fastlane"
gem 'mustache' # for config generator
gem 'xcode-install'
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)

View File

@ -1,91 +0,0 @@
GREEN := $(shell tput -Txterm setaf 2)
YELLOW := $(shell tput -Txterm setaf 3)
WHITE := $(shell tput -Txterm setaf 7)
RESET := $(shell tput -Txterm sgr0)
RUBY_VERSION="2.7.6"
open_project=(open *.xcworkspace)
install_dev_certs=(bundle exec fastlane InstallDevelopmentSigningIdentities)
install_pods=(bundle exec pod install || bundle exec pod install --repo-update)
init_rbenv=(if command -v rbenv &> /dev/null; then eval "$$(rbenv init -)"; fi)
TARGET_MAX_CHAR_NUM=20
## Show help
help:
@echo ''
@echo 'Использование:'
@echo ' ${YELLOW}make${RESET} ${GREEN}<target>${RESET}'
@echo ''
@echo 'Команды:'
@awk '/^[a-zA-Z\-\_0-9]+:/ { \
helpMessage = match(lastLine, /^## (.*)/); \
if (helpMessage) { \
helpCommand = substr($$1, 0, index($$1, ":")-1); \
helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
printf " ${YELLOW}%-$(TARGET_MAX_CHAR_NUM)s${RESET} ${GREEN}%s${RESET}\n", helpCommand, helpMessage; \
} \
} \
{ lastLine = $$0 }' $(MAKEFILE_LIST)
## Инициализирует проект и устанавливает системные утилиты
init:
brew bundle
$(call init_rbenv)
rbenv install -s ${RUBY_VERSION}
rbenv global ${RUBY_VERSION}
if ! gem spec bundler > /dev/null 2>&1; then\
echo "bundler gem is not installed!";\
-sudo gem install bundler;\
fi
bundle install
xcodegen
$(call install_pods)
bundle exec fastlane install_plugins
$(call install_dev_certs)
$(call open_project)
git config --local core.hooksPath .githooks
## Устанавливает поды
pod:
$(call install_pods)
## Запускает генерацию файла проекта
gen:
xcodegen
## Устанавливает сертификат и профили для запуска на девайсе
dev_certs:
$(call install_dev_certs)
## Открывает папку для ручного редактирования сертификатов и профайлов
update_certs:
bundle exec fastlane ManuallyUpdateCodeSigning
## Поднимает версию приложения (параметр "X.Y.Z")
bumpAppVersion:
ifeq ($(version),undefined)
@echo "Version parameter is missing (ex: x.y.z)" $(target)
else
bundle exec fastlane run increment_version_number version_number:$(version)
endif
## Позволяет быстро открыть workspace проекта
start:
$(call open_project)
## Очищает содержимое папки DerivedData
clean:
rm -rf ~/Library/Developer/Xcode/DerivedData/*

View File

@ -1,25 +0,0 @@
#!/bin/sh
# Description:
# Runs full linting and copy-paste-detection for project
#
# Required environment variables:
# SRCROOT - project directory.
# SCRIPT_DIR - directory of current script.
#
# Optional environment variables:
# See swiftlint.sh and copy_paste_detection.sh for complete list of available variables
#
# Example of usage:
# ./full_code_lint.sh
#
if [ -z "${SCRIPT_DIR}" ]; then
SCRIPT_DIR=${SRCROOT}/build-scripts/xcode/build_phases
fi
. ${SRCROOT}/build-scripts/xcode/aux_scripts/install_env.sh swiftlint
FORCE_LINT=true; . ${SCRIPT_DIR}/swiftlint.sh
. ${SRCROOT}/build-scripts/xcode/aux_scripts/install_env.sh cpd
. ${SCRIPT_DIR}/copy_paste_detection.sh Localization Generated Pods

View File

@ -1,22 +0,0 @@
#!/bin/sh
# Description:
# Runs incremental linting for project
#
# Required environment variables:
# SRCROOT - project directory.
# SCRIPT_DIR - directory of current script.
#
# Optional environment variables:
# See swiftlint.sh for complete list of available variables
#
# Example of usage:
# ./incremetal_code_lint.sh
#
if [ -z "${SCRIPT_DIR}" ]; then
SCRIPT_DIR=${SRCROOT}/build-scripts/xcode/build_phases
fi
. ${SRCROOT}/build-scripts/xcode/aux_scripts/install_env.sh swiftlint
. ${SCRIPT_DIR}/swiftlint.sh

View File

@ -1,6 +0,0 @@
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_DIR="${DIR}/../../../"
make init -C ${PROJECT_DIR}

355
xcode/build_phases/api_generator.sh Executable file → Normal file
View File

@ -1,350 +1,9 @@
#!/bin/sh VERSION=$1
FILE_NAME="api-generator-${VERSION}.jar"
# Description: # download api generator
# Generates API models & methods. link="https://dl.bintray.com/touchin/touchin-tools/ru/touchin/api-generator/${VERSION}/${FILE_NAME}"
# . build-scripts/xcode/aux_scripts/download_file.sh ${FILE_NAME} ${link}
# Parameters:
# $1 - api generator version.
# $2 - path to generated code directory
#
# Required environment variables:
# SRCROOT - path to project folder.
#
# Optional environment variables:
# OUTPUT_PATH - path to Generated folder.
# API_SPEC_DIR - path to api specification folder
# VERBOSE - print debug messages
# API_NAME - project name that will be used by generator (example: OUTPUT_PATH/API_NAME/Classes )
#
# Examples of usage:
# . api_generator.sh 1.4.0-beta1
# . api_generator.sh 1.4.0-beta1 ${TARGET_NAME}/Generated
#
readonly EXIT_SUCCESS=0 # execute api generator
readonly EXIT_FAILURE=1 java -Xmx6g -jar "Downloads/${FILE_NAME}" generate-client-code --output-language SWIFT --specification-path common/api --output-path ${PRODUCT_NAME}/Generated --single-file true
readonly TRUE=1
readonly FALSE=0
readonly LOG_TAG="API-GENERATOR"
notice()
{
echo "${LOG_TAG}:NOTICE: ${1}" >&2
}
debug()
{
if [ ! -z "${VERBOSE}" ]; then
echo "${LOG_TAG}:DEBUG: ${1}" >&2
fi
}
exit_on_failure()
{
eval ${1}
local -r EXIT_CODE=$?
if [ ${EXIT_CODE} -ne 0 ]; then
echo "Recent command: \`${1}\` failed with code ${EXIT_CODE}"
exit ${EXIT_CODE}
fi
}
is_force_run()
{
if [ -z "${FORCE_RUN}" ]; then
echo ${FALSE}
return
fi
local -r STR_MODE=`tr "[:upper:]" "[:lower:]" <<< ${FORCE_RUN}`
if [ ${STR_MODE} == "yes" ] || [ ${STR_MODE} == "true" ] || [ ${STR_MODE} == "1" ]; then
echo ${TRUE}
else
echo ${FALSE}
fi
}
is_single_file()
{
if [ -z "${SINGLE_FILE}" ]; then
echo "true"
return
fi
local -r STR_MODE=`tr "[:upper:]" "[:lower:]" <<< ${SINGLE_FILE}`
if [ ${STR_MODE} == "no" ] || [ ${STR_MODE} == "false" ] || [ ${STR_MODE} == "0" ]; then
echo "false"
else
echo "true"
fi
}
get_api_spec_current_commit()
{
if [ -z "${API_SPEC_DIR}" ]; then
if [ ! -z "${1}" ]; then
echo `git -C ${1} rev-parse --verify HEAD`
else
echo `git rev-parse --verify HEAD`
fi
else
echo `git -C ${API_SPEC_DIR} rev-parse --verify HEAD`
fi
}
get_api_spec_status()
{
if [ -z "${API_SPEC_DIR}" ]; then
if [ ! -z "${1}" ]; then
echo `git -C ${1} status -s`
else
echo `git status -s`
fi
else
echo `git -C ${API_SPEC_DIR} status -s`
fi
}
is_api_spec_under_source_control()
{
local IS_UNDER_SOURCE_CONTROL_CHECK
if [ -z "${API_SPEC_DIR}" ]; then
if [ ! -z "${1}" ]; then
IS_UNDER_SOURCE_CONTROL_CHECK=`git -C ${1} rev-parse --is-inside-work-tree 2>/dev/null`
else
IS_UNDER_SOURCE_CONTROL_CHECK=`git rev-parse --is-inside-work-tree 2>/dev/null`
fi
else
IS_UNDER_SOURCE_CONTROL_CHECK=`git -C ${API_SPEC_DIR} rev-parse --is-inside-work-tree 2>/dev/null`
fi
if [ "${IS_UNDER_SOURCE_CONTROL_CHECK}" = "true" ]; then
echo ${TRUE}
else
echo ${FALSE}
fi
}
is_api_spec_has_uncommited_changes()
{
if [ `is_api_spec_under_source_control` -eq ${TRUE} ]; then
local -r API_SPEC_STATUS=`get_api_spec_status`
if [ -z "${API_SPEC_STATUS}" ]; then
echo ${FALSE}
else
echo ${TRUE}
fi
else
echo ${FALSE}
fi
}
is_nothing_changed_since_last_check()
{
if [ `is_force_run` -eq ${TRUE} ]; then
notice "Force run detected. Skipping commits comparison."
echo ${TRUE}
fi
if [ -z "${COMMIT_FILE_PATH}" ]; then
if [ ! -z "${1}" ]; then
local -r COMMIT_FILE_PATH=${1}
else
debug "COMMIT_FILE_PATH should be defined or passed as first argument!"
echo ${FALSE}
fi
fi
if [ `is_api_spec_under_source_control` -eq ${TRUE} ]; then
local -r CURRENT_COMMIT=`get_api_spec_current_commit`
local -r LAST_CHECKED_COMMIT=`cat ${COMMIT_FILE_PATH} 2> /dev/null || echo ""`
if [ ${CURRENT_COMMIT} = "${LAST_CHECKED_COMMIT}" ]; then
if [ `is_api_spec_has_uncommited_changes` -eq ${TRUE} ]; then
notice "API spec has uncomitted changes."
echo ${FALSE}
else
echo ${TRUE}
fi
else
echo ${FALSE}
fi
else
echo ${FALSE}
fi
}
record_current_commit()
{
if [ `is_force_run` -eq ${TRUE} ]; then
notice "Force run detected. Commit won't be recorder."
exit ${EXIT_SUCCESS}
fi
if [ -z "${COMMIT_FILE_PATH}" ]; then
if [ ! -v "${1}" ]; then
local -r COMMIT_FILE_PATH=${1}
else
debug "COMMIT_FILE_PATH should be defined or passed as second argument!"
return ${EXIT_FAILURE}
fi
fi
local -r CURRENT_COMMIT=`get_api_spec_current_commit`
echo ${CURRENT_COMMIT} > ${COMMIT_FILE_PATH}
}
openapi_codegen()
{
if [ -z "${OPEN_API_SPEC_PATH}" ]; then
if [ ! -v "${1}" ]; then
local -r OPEN_API_SPEC_PATH=${1}
else
debug "OPEN_API_SPEC_PATH should be defined or passed as first argument!"
return ${EXIT_FAILURE}
fi
fi
if [ -z "${OUTPUT_PATH}" ]; then
if [ ! -v "${2}" ]; then
local -r OUTPUT_PATH=${2}
else
debug "OUTPUT_PATH should be defined or passed as second argument!"
return ${EXIT_FAILURE}
fi
fi
if [ -z "${VERSION}" ]; then
if [ ! -v "${3}" ]; then
local -r VERSION=${3}
else
debug "VERSION should be defined or passed as third argument!"
return ${EXIT_FAILURE}
fi
fi
if [ -z "${API_NAME}" ]; then
local -r API_NAME="${PROJECT_NAME}API"
fi
notice "OpenAPI spec generation for ${OPEN_API_SPEC_PATH}"
local -r CODEGEN_VERSION="3.0.34"
local -r CODEGEN_FILE_NAME="swagger-codegen-cli-${CODEGEN_VERSION}.jar"
local -r CODEGEN_DOWNLOAD_URL="https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/${CODEGEN_VERSION}/${CODEGEN_FILE_NAME}"
. build-scripts/xcode/aux_scripts/download_file.sh ${CODEGEN_FILE_NAME} ${CODEGEN_DOWNLOAD_URL}
local -r TINETWORKING_CODEGEN_FILE_NAME="codegen-${VERSION}.jar"
local -r DOWNLOAD_URL="https://maven.dev.touchin.ru/ru/touchin/codegen/${VERSION}/${TINETWORKING_CODEGEN_FILE_NAME}"
. build-scripts/xcode/aux_scripts/download_file.sh ${TINETWORKING_CODEGEN_FILE_NAME} ${DOWNLOAD_URL}
rm -rf ${OUTPUT_PATH}/${API_NAME} # remove previously generated API (if exists)
local -r OPENAPI_COMMAND="java -cp "Downloads/${CODEGEN_FILE_NAME}:Downloads/${TINETWORKING_CODEGEN_FILE_NAME}" io.swagger.codegen.v3.cli.SwaggerCodegen generate -l TINetworking -i ${OPEN_API_SPEC_PATH} -o ${OUTPUT_PATH} --additional-properties projectName=${API_NAME}"
exit_on_failure "${OPENAPI_COMMAND}"
# flatten folders hierarchy
mv ${OUTPUT_PATH}/${API_NAME}/Classes/Swaggers/* ${OUTPUT_PATH}/${API_NAME}/
rm -rf ${OUTPUT_PATH}/${API_NAME}/Classes
}
api_generator_codegen()
{
if [ -z "${API_SPEC_DIR}" ]; then
if [ ! -v "${1}" ]; then
local -r API_SPEC_DIR=${1}
else
debug "API_SPEC_DIR should be defined or passed as first argument!"
return ${EXIT_FAILURE}
fi
fi
if [ -z "${OUTPUT_PATH}" ]; then
if [ ! -v "${2}" ]; then
local -r OUTPUT_PATH=${2}
else
debug "OUTPUT_PATH should be defined or passed as second argument!"
return ${EXIT_FAILURE}
fi
fi
if [ -z "${VERSION}" ]; then
if [ ! -v "${3}" ]; then
local -r VERSION=${3}
else
debug "VERSION should be defined or passed as third argument!"
return ${EXIT_FAILURE}
fi
fi
notice "api-generator spec generation for ${API_SPEC_DIR}/main.json"
local -r FILE_NAME="api-generator-${VERSION}.jar"
local -r DOWNLOAD_URL="https://maven.dev.touchin.ru/ru/touchin/api-generator/${VERSION}/${FILE_NAME}"
. build-scripts/xcode/aux_scripts/download_file.sh ${FILE_NAME} ${DOWNLOAD_URL}
local -r API_GENERATOR_COMMAND="java -Xmx12g -jar Downloads/${FILE_NAME} generate-client-code --output-language SWIFT --specification-path ${API_SPEC_DIR} --output-path ${OUTPUT_PATH} --single-file $(is_single_file)"
exit_on_failure "${API_GENERATOR_COMMAND}"
}
readonly BUILD_PHASES_DIR=${SRCROOT}/build_phases
mkdir -p ${BUILD_PHASES_DIR}
readonly COMMIT_FILE_PATH=${BUILD_PHASES_DIR}/api-generator-commit
if [ `is_nothing_changed_since_last_check` -eq ${TRUE} ]; then
notice "Nothing was changed. API generation skipped."
exit ${EXIT_SUCCESS}
fi
readonly VERSION=$1
if [ -z "${OUTPUT_PATH}" ]; then
if [ ! -z "${2}" ]; then
readonly OUTPUT_PATH=${2}
else
readonly OUTPUT_PATH="Generated"
fi
fi
if [ -z "${API_SPEC_DIR}" ]; then
readonly API_SPEC_DIR="common/api"
fi
mkdir -p ${OUTPUT_PATH}
readonly OPEN_API_SPEC_PATH=`find ${API_SPEC_DIR} -maxdepth 1 -name '*.yaml' -o -name '*.yml' | head -n 1`
if [ -f "${OPEN_API_SPEC_PATH}" ]; then
openapi_codegen
elif [ -f "${API_SPEC_DIR}/main.json" ]; then
api_generator_codegen
else
notice "No api spec found!"
exit ${EXIT_FAILURE}
fi
if [ $? -ne ${EXIT_FAILURE} ]; then
record_current_commit
fi

View File

@ -1,89 +0,0 @@
#!/bin/sh
# Description:
# Converts SCRIPT_INPUT_FILE_{N} or SCRIPT_INPUT_FILE_LIST_{N} variables to string that contains
# list of file names splitted by given separator.
#
# Parameters:
# $1 - separator to use.
# $2 - default value to return if SCRIPT_INPUT_FILE_COUNT or SCRIPT_INPUT_FILE_LIST_COUNT is zero.
#
# Optional environment variables:
# FILE_NAMES_SEPARATOR - separator to use.
# DEFAULT_FILE_NAMES - default value if was found in environment variables.
# SCRIPT_INPUT_FILE_COUNT - number of files listed in "Input files" section of build phase.
# SCRIPT_INPUT_FILE_{N} - file path of specific input file at index.
# SCRIPT_INPUT_FILE_LIST_COUNT - number of files listed in "Input File Lists" section of build phase.
# SCRIPT_INPUT_FILE_LIST_{N} - file path to specifis xcfilelist file at index.
#
# Examples of usage:
# read_input_file_names
# read_input_file_names.sh " " path/to/project
#
has_input_files()
{
[ ! -z "${SCRIPT_INPUT_FILE_COUNT}" ] && [ ${SCRIPT_INPUT_FILE_COUNT} -gt 0 ]
}
has_input_file_lists()
{
[ ! -z "${SCRIPT_INPUT_FILE_LIST_COUNT}" ] && [ ${SCRIPT_INPUT_FILE_LIST_COUNT} -gt 0 ]
}
if [ -z "${FILE_NAMES_SEPARATOR}" ]; then
if [ ! -z "${1}" ]; then
FILE_NAMES_SEPARATOR=${1}
else
FILE_NAMES_SEPARATOR=" "
fi
fi
if [ -z "${DEFAULT_FILE_NAMES}" ]; then
if [ ! -z "${2}" ]; then
DEFAULT_FILE_NAMES=${2}
else
DEFAULT_FILE_NAMES=""
fi
fi
INPUT_FILE_NAMES=""
if has_input_files && has_input_file_lists; then
>&2 echo "Passing Input Files and Input Files Lists is not supported!\nOnly Input Files will be used."
fi
if has_input_files && \
[ ${SCRIPT_INPUT_FILE_COUNT} -gt 0 ]; then
for i in `seq 0 $((${SCRIPT_INPUT_FILE_COUNT}-1))`
do
SCRIPT_INPUT_FILE_VARIABLE_NAME="SCRIPT_INPUT_FILE_${i}"
SHELL_VARIABLE="\${${SCRIPT_INPUT_FILE_VARIABLE_NAME}}"
RESOLVED_FILE_NAME=`envsubst <<< ${SHELL_VARIABLE}`
if [ ! -z ${INPUT_FILE_NAMES} ]; then
INPUT_FILE_NAMES=${INPUT_FILE_NAMES}${FILE_NAMES_SEPARATOR}
else
INPUT_FILE_NAMES=${INPUT_FILE_NAMES}${FILE_NAMES_SEPARATOR}${RESOLVED_FILE_NAME}
fi
done
elif has_input_file_lists; then
for i in `seq 0 $((${SCRIPT_INPUT_FILE_LIST_COUNT}-1))`
do
SCRIPT_INPUT_FILE_LIST_VARIABLE_NAME="SCRIPT_INPUT_FILE_LIST_${i}"
SHELL_VARIABLE="\${${SCRIPT_INPUT_FILE_LIST_VARIABLE_NAME}}"
FILE_NAME=`envsubst <<< ${SHELL_VARIABLE}`
RESOLVED_FILE_NAMES=`envsubst < ${FILE_NAME}`
for INPUT_FILE_NAME in ${RESOLVED_FILE_NAMES}; do
INPUT_FILE_NAMES=${INPUT_FILE_NAMES}${INPUT_FILE_NAME}${FILE_NAMES_SEPARATOR}
done
done
fi
if [ -z "${INPUT_FILE_NAMES}" ]; then
echo ${DEFAULT_FILE_NAMES}
else
echo ${INPUT_FILE_NAMES}
fi

65
xcode/build_phases/copy_paste_detection.sh Executable file → Normal file
View File

@ -1,65 +1,20 @@
#!/bin/sh
# Description:
# Validates code for copy-paste, prints results to standard output and report file.
#
# Parameters:
# $1 $2 $3 $n - folders to exclude from code checking.
#
# Required environment variables:
# SRCROOT - project directory.
# SCRIPT_DIR - directory of current script.
#
# Optional environment variables:
# SCRIPT_INPUT_FILE_COUNT - number of files listed in "Input files" of build phase.
# SCRIPT_INPUT_FILE_{N} - file path to directory that should be checked.
#
# Modified files:
# ${SRCROOT}/code-quality-reports/CPDLog.txt - check report.
#
# Example of usage:
# copy_paste_detection.sh Generated Localization Pods
#
EXIT_SUCCESS=0
EXIT_FAILURE=1
if which pmd >/dev/null; then if which pmd >/dev/null; then
REPORTS_DIR="${SRCROOT}/code-quality-reports" # running CPD
readonly SOURCES_DIR=${1:-${PROJECT_DIR}} # first argument or PROJECT_DIR
readonly REPORTS_DIR=${PROJECT_DIR}/code-quality-reports
readonly FILES_TO_EXCLUDE=`find ${SOURCES_DIR} -type d -name Localization -or -name Generated -or -name Carthage -or -name Pods | paste -sd " " -`
SOURCES_DIRS=`. ${SCRIPT_DIR}/common/read_input_file_names.sh " " ${SRCROOT}` mkdir ${REPORTS_DIR}
COMMAND_LINE_ARGUMENTS=$@ pmd cpd --files ${SOURCES_DIR} --exclude ${FILES_TO_EXCLUDE} --minimum-tokens 50 --language swift --encoding UTF-8 --format net.sourceforge.pmd.cpd.XMLRenderer > ${REPORTS_DIR}/cpd-output.xml --failOnViolation true
FOLDERS_TO_EXCLUDE="" php ./build-scripts/xcode/aux_scripts/cpd_script.php ${REPORTS_DIR}/cpd-output.xml | tee ${REPORTS_DIR}/CPDLog.txt
for argument in ${COMMAND_LINE_ARGUMENTS} # Make paths relative to SOURCES_DIR, so different developers won't rewrite entire file
do readonly SED_REPLACEMENT_STRING=$(echo ${SOURCES_DIR} | sed "s/\//\\\\\//g")
FOLDERS_TO_EXCLUDE=${FOLDERS_TO_EXCLUDE}"-or -name ${argument} "
done
FOLDERS_TO_EXCLUDE=`echo ${FOLDERS_TO_EXCLUDE} | cut -c5-` # remove first "-or"
FILES_TO_EXCLUDE=`find ${SRCROOT} -type d ${FOLDERS_TO_EXCLUDE} | paste -sd " " -`
mkdir -p ${REPORTS_DIR}
DIRS_ARGUMENTS=""
for SOURCE_DIR in ${SOURCES_DIRS}; do
DIRS_ARGUMENTS=${DIRS_ARGUMENTS}" --dir "${SOURCE_DIR}
done
pmd cpd ${DIRS_ARGUMENTS} --exclude ${FILES_TO_EXCLUDE} --minimum-tokens 50 --language swift --encoding UTF-8 --format net.sourceforge.pmd.cpd.XMLRenderer --skip-lexical-errors true > ${REPORTS_DIR}/cpd-output.xml
php ${SCRIPT_DIR}/../aux_scripts/cpd_script.php ${REPORTS_DIR}/cpd-output.xml | tee ${REPORTS_DIR}/CPDLog.txt
# Make paths relative to SRCROOT, so different developers won't rewrite entire file
SED_REPLACEMENT_STRING=$(echo ${SRCROOT} | sed "s/\//\\\\\//g")
sed -i '' "s/${SED_REPLACEMENT_STRING}//g" "${REPORTS_DIR}/CPDLog.txt" sed -i '' "s/${SED_REPLACEMENT_STRING}//g" "${REPORTS_DIR}/CPDLog.txt"
else else
echo "warning: pmd not installed, install using 'brew install pmd'" echo "warning: pmd not installed, install using 'brew install pmd'"
exit 1
exit ${EXIT_FAILURE}
fi fi

View File

@ -1,22 +0,0 @@
require 'yaml'
require_relative '../../managers/managers'
require_relative '../../templates/templates'
# Input files paths
build_settings_file_path = ARGV[0]
generated_features_enum_file_path = ARGV[1]
build_settings_features_list = Managers::FileManager.load_from_file_YAML(build_settings_file_path)["features"]
if build_settings_features_list.nil? or build_settings_features_list.empty?
raise "There are no features in " + build_settings_file_path
end
# Generate enum Feature Toggles
features_enum_template = Templates::FeatureTemplates.features_enum
utils = Managers::TemplateManager.new(build_settings_features_list)
rendered_enum = utils.render(features_enum_template).strip
Managers::FileManager.save_data_to_file(generated_features_enum_file_path, rendered_enum)

View File

@ -1,19 +0,0 @@
# Input paths
readonly BUILD_SETTINGS_FILE_PATH=${1:-${PROJECT_DIR}/common/build_settings.yaml}
readonly FEATURES_ENUM_FILE_PATH=${2:-${PROJECT_DIR}/${PRODUCT_NAME}/Resources/Features/Feature.swift}
# Features enunm generator script
readonly CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
readonly GENERATOR_SCRIPT=${CURRENT_DIR}/features_generator.rb
if ! [ -e ${BUILD_SETTINGS_FILE_PATH} ]; then
echo "File ${BUILD_SETTINGS_FILE_PATH} does not exist. Add this file and try again."
exit 1
fi
if ! [ -e ${FEATURES_ENUM_FILE_PATH} ]; then
echo "File ${FEATURES_ENUM_FILE_PATH} does not exist. Add this file and try again."
exit 1
fi
ruby ${GENERATOR_SCRIPT} ${BUILD_SETTINGS_FILE_PATH} ${FEATURES_ENUM_FILE_PATH}

View File

@ -1,47 +1,16 @@
#!/bin/sh LOCALIZATION_PATH="${PRODUCT_NAME}/Resources/Localization"
#first argument set strings folder path
# Description:
# Generates Localizeable.strings and String+Localization.swift files.
#
# Parameters:
# $1 - path to strings folder containing json files.
# $2 - path to Localization folder (output).
# $3 - Bundle for localization. Default is `.main`.
#
# Required environment variables:
# SCRIPT_DIR - directory of current script.
#
# Optional environment variables:
# PRODUCT_NAME - product name to produce path to localization folder (output).
#
# Examples of usage:
# . localization.sh
# . localization.sh common/strings Resources/Localization/ .main
#
readonly EXIT_SUCCESS=0
readonly EXIT_FAILURE=1
. ${SCRIPT_DIR}/../aux_scripts/install_env.sh php
STRINGS_FOLDER=${1:-"common/strings"} STRINGS_FOLDER=${1:-"common/strings"}
LOCALIZATION_PATH=${2:-"${PRODUCT_NAME}/Resources/Localization/"}
BUNDLE=${3:-".main"}
if ! [ -e ${LOCALIZATION_PATH} ]; then if ! [ -e ${LOCALIZATION_PATH} ]; then
echo "${LOCALIZATION_PATH} path does not exist. Add these folders and try again." echo "${PROJECT_DIR}/${LOCALIZATION_PATH} path does not exist. Add these folders and try again."
exit ${EXIT_FAILURE} exit 1
fi fi
if ! [ -e "${STRINGS_FOLDER}" ]; then if ! [ -e "${PROJECT_DIR}/${STRINGS_FOLDER}" ]; then
echo "${STRINGS_FOLDER} path does not exist. Submodule with strings should be named common and contain strings folder." echo "${PROJECT_DIR}/${STRINGS_FOLDER} path does not exist. Submodule with strings should be named common and contain strings folder."
exit ${EXIT_FAILURE} exit 1
fi fi
if which php >/dev/null; then #second argument set strings script path
php ${SCRIPT_DIR}/../aux_scripts/import_strings.php ${LOCALIZATION_PATH} ${STRINGS_FOLDER} ${BUNDLE} php ${2:-build-scripts/xcode/aux_scripts/import_strings.php} ${PRODUCT_NAME} ${STRINGS_FOLDER}
else
echo "warning: php not installed, install using 'brew install php'"
exit ${EXIT_FAILURE}
fi

View File

@ -7,50 +7,17 @@ class SettingOption
def initialize def initialize
@options = OpenStruct.new @options = OpenStruct.new
OptionParser.new do |opt| OptionParser.new do |opt|
opt.on('-p', opt.on('-p', '--project_root_path STRING', 'The path of project directory and contains *.xcodeproj file always. ' +
'--project_root_path STRING', 'Example: project_root_path=~/Projects/MyProject/Source/..') { |option| @options.project_root_path = option }
'The path of project directory and contains *.xcodeproj file always. ' + opt.on('-r', '--source_root_path STRING', 'The path of source directory and may not contains *.xcodeproj file in some cases. ' +
'Example: project_root_path=~/Projects/MyProject/Source/..') { |option| 'Example: source_root_path=~/Projects/MyProject/') { |option| @options.source_root_path = option }
@options.project_root_path = option opt.on('-s', '--swiftlint_executable_path STRING', 'The executable path of swiftlint') { |option| @options.swiftlint_executable_path = option }
} opt.on('-c', '--check_mode MODE', 'The mode of check is "fully" or "simplified"') { |option| @options.check_mode = option }
opt.on('-r', opt.on('-u', '--use_multiple BOOL', 'The flag indicates the use of multiple yaml swiftlint configurations') { |option| @options.use_multiple = option }
'--source_root_path STRING', opt.on('-d', '--source_date DATE', 'The date of grouping files according touchin and old swiftlint rules') { |option| @options.source_date = option }
'The path of source directory and may not contains *.xcodeproj file in some cases. ' + opt.on('-y', '--touchin_swiftlint_yaml_path STRING', 'The path to the touchin swiftlint yaml relative to the source directory') { |option| @options.touchin_swiftlint_yaml_path = option }
'Example: source_root_path=~/Projects/MyProject/') { |option|
@options.source_root_path = option
}
opt.on('-s',
'--swiftlint_executable_path STRING',
'The executable path of swiftlint') { |option|
@options.swiftlint_executable_path = option
}
opt.on('-c',
'--check_mode MODE',
'The mode of check is "fully" or "simplified"') { |option|
@options.check_mode = option
}
opt.on('-u',
'--use_multiple BOOL',
'The flag indicates the use of multiple yaml swiftlint configurations') { |option|
@options.use_multiple = option
}
opt.on('-d',
'--source_date DATE',
'The date of grouping files according touchin and old swiftlint rules') { |option|
@options.source_date = option
}
opt.on('-tc',
'--touchin_swiftlint_yaml_path STRING',
'The path to the touchin swiftlint yaml relative to the source directory') { |option|
@options.touchin_swiftlint_yaml_path = option
}
opt.on('-oc',
'--old_swiftlint_yaml_path STRING',
'The path to the old swiftlint yaml relative to the source directory') { |option|
@options.old_swiftlint_yaml_path = option
}
end.parse! end.parse!
if @options.check_mode.to_s.nilOrEmpty? if @options.check_mode.to_s.nilOrEmpty?
@options.check_mode = 'fully' @options.check_mode = 'fully'
end end
@ -62,17 +29,37 @@ class SettingOption
if @options.source_root_path.to_s.nilOrEmpty? if @options.source_root_path.to_s.nilOrEmpty?
@options.source_root_path = @options.project_root_path @options.source_root_path = @options.project_root_path
end end
if @options.touchin_swiftlint_yaml_path.to_s.nilOrEmpty? if @options.touchin_swiftlint_yaml_path.to_s.nilOrEmpty?
@options.touchin_swiftlint_yaml_path = File.join(project_root_path, 'build-scripts/xcode/.swiftlint.yml') @options.touchin_swiftlint_yaml_path = '/build-scripts/xcode/.swiftlint.yml'
end
if @options.old_swiftlint_yaml_path.to_s.nilOrEmpty?
@options.old_swiftlint_yaml_path = File.join(project_root_path, '.swiftlint.yml')
end end
end end
def method_missing(method, *args, &block) def project_root_path
@options.send(method, *args, &block) @options.project_root_path
end
def source_date
@options.source_date
end
def swiftlint_executable_path
@options.swiftlint_executable_path
end
def check_mode
@options.check_mode
end
def use_multiple
@options.use_multiple
end
def source_root_path
@options.source_root_path
end
def touchin_swiftlint_yaml_path
@options.touchin_swiftlint_yaml_path
end end
end end

View File

@ -1,5 +1,4 @@
require 'fileutils' require 'fileutils'
require 'tmpdir'
require_relative 'array_extension.rb' require_relative 'array_extension.rb'
require_relative 'git_caretaker.rb' require_relative 'git_caretaker.rb'
@ -8,19 +7,19 @@ require_relative 'swift_file_manager.rb'
require_relative 'yaml_manager.rb' require_relative 'yaml_manager.rb'
class StrategyMaker class StrategyMaker
def initialize(project_root_path, swiftlint_executable_path, touchin_swiftlint_yaml_path, old_swiftlint_yaml_path) def initialize(project_root_path, swiftlint_executable_path, touchin_swiftlint_yaml_path)
@project_root_path = project_root_path @project_root_path = project_root_path
@touchin_swiftlint_yaml_path = touchin_swiftlint_yaml_path @touchin_swiftlint_yaml_path = project_root_path + touchin_swiftlint_yaml_path
@old_swiftlint_yaml_path = old_swiftlint_yaml_path @old_swiftlint_yaml_path = project_root_path + '/.swiftlint.yml'
@temporary_swiftlint_folder_name = Dir.mktmpdir @temporary_swiftlint_folder_name = project_root_path + '/temporary_swiftlint'
@touchin_swiftlint_yaml_temporary_path = File.join(@temporary_swiftlint_folder_name, '.touchin_swiftlint.yml') @touchin_swiftlint_yaml_temporary_path = @temporary_swiftlint_folder_name + '/.touchin_swiftlint.yml'
@old_swiftlint_yaml_temporary_path = File.join(@temporary_swiftlint_folder_name, '.old_swiftlint.yml') @old_swiftlint_yaml_temporary_path = @temporary_swiftlint_folder_name + '/.old_swiftlint.yml'
@swiftlint_autocorrect_command = swiftlint_executable_path + ' autocorrect --path ' + @project_root_path + ' --config ' @swiftlint_autocorrect_command = swiftlint_executable_path + ' autocorrect --path ' + @project_root_path + ' --config '
@swiftlint_lint_command = swiftlint_executable_path + ' --path ' + @project_root_path + ' --config ' @swiftlint_lint_command = swiftlint_executable_path + ' --path ' + @project_root_path + ' --config '
end end
def run_fully_multiple_strategy(source_date) def run_fully_multiple_strategy(source_date)
create_yaml_managers_and_copy_temporary_files create_yaml_managers_and_copy_temporary_files
@ -34,38 +33,38 @@ class StrategyMaker
@touchin_swiftlint_yaml_manager.update('excluded', total_touchin_excluded_files) @touchin_swiftlint_yaml_manager.update('excluded', total_touchin_excluded_files)
@old_swiftlint_yaml_manager.update('excluded', total_old_excluded_files) @old_swiftlint_yaml_manager.update('excluded', total_old_excluded_files)
run_multiple_strategy(@touchin_swiftlint_yaml_temporary_path, @old_swiftlint_yaml_temporary_path) run_multiple_strategy(@touchin_swiftlint_yaml_temporary_path, @old_swiftlint_yaml_temporary_path)
end end
def run_simplified_multiple_strategy(source_date, source_root_path) def run_simplified_multiple_strategy(source_date, source_root_path)
included_files = GitСaretaker.get_modified_files included_files = GitСaretaker.get_modified_files
if included_files.nilOrEmpty? if included_files.nilOrEmpty?
puts 'Git did not found swift files to check' puts 'Git did not found swift files to check'
return return
end end
create_yaml_managers_and_copy_temporary_files create_yaml_managers_and_copy_temporary_files
exclude_files = unique_exclude_files(@touchin_swiftlint_yaml_manager, @old_swiftlint_yaml_manager) exclude_files = unique_exclude_files(@touchin_swiftlint_yaml_manager, @old_swiftlint_yaml_manager)
included_files = included_files.map { |file_path| source_root_path + file_path } included_files = included_files.map { |file_path| source_root_path + file_path }
swift_file_manager = SwiftFileManager.new(exclude_files, source_date) swift_file_manager = SwiftFileManager.new(exclude_files, source_date)
swift_file_manager.find_list_file_paths_from(included_files) swift_file_manager.find_list_file_paths_from(included_files)
total_touchin_included_files = swift_file_manager.new_files total_touchin_included_files = swift_file_manager.new_files
total_old_included_files = swift_file_manager.old_files total_old_included_files = swift_file_manager.old_files
@touchin_swiftlint_yaml_manager.update('excluded', []) @touchin_swiftlint_yaml_manager.update('excluded', [])
@old_swiftlint_yaml_manager.update('excluded', []) @old_swiftlint_yaml_manager.update('excluded', [])
@touchin_swiftlint_yaml_manager.update('included', total_touchin_included_files) @touchin_swiftlint_yaml_manager.update('included', total_touchin_included_files)
@old_swiftlint_yaml_manager.update('included', total_old_included_files) @old_swiftlint_yaml_manager.update('included', total_old_included_files)
is_exist_total_touchin_included_files = (not total_touchin_included_files.nilOrEmpty?) is_exist_total_touchin_included_files = (not total_touchin_included_files.nilOrEmpty?)
is_exist_total_old_included_files = (not total_old_included_files.nilOrEmpty?) is_exist_total_old_included_files = (not total_old_included_files.nilOrEmpty?)
if is_exist_total_touchin_included_files and is_exist_total_old_included_files if is_exist_total_touchin_included_files and is_exist_total_old_included_files
run_multiple_strategy(@touchin_swiftlint_yaml_temporary_path, @old_swiftlint_yaml_temporary_path) run_multiple_strategy(@touchin_swiftlint_yaml_temporary_path, @old_swiftlint_yaml_temporary_path)
elsif is_exist_total_touchin_included_files and not is_exist_total_old_included_files elsif is_exist_total_touchin_included_files and not is_exist_total_old_included_files
@ -76,11 +75,11 @@ class StrategyMaker
puts 'Git did not found swift files to check' puts 'Git did not found swift files to check'
end end
end end
def run_fully_single_strategy def run_fully_single_strategy
run_single_strategy(@touchin_swiftlint_yaml_path) run_single_strategy(@touchin_swiftlint_yaml_path)
end end
def run_simplified_single_strategy(source_root_path) def run_simplified_single_strategy(source_root_path)
included_files = GitСaretaker.get_modified_files included_files = GitСaretaker.get_modified_files
@ -88,34 +87,34 @@ class StrategyMaker
puts 'Git did not found swift files to check' puts 'Git did not found swift files to check'
return return
end end
create_copy_temporary_touchin_files create_copy_temporary_touchin_files
touchin_swiftlint_yaml_manager = YamlManager.new(@touchin_swiftlint_yaml_temporary_path) touchin_swiftlint_yaml_manager = YamlManager.new(@touchin_swiftlint_yaml_temporary_path)
touchin_excluded_files = touchin_swiftlint_yaml_manager.get_configuration('excluded') touchin_excluded_files = touchin_swiftlint_yaml_manager.get_configuration('excluded')
swift_files = SwiftFileManager.new(touchin_excluded_files, '') swift_files = SwiftFileManager.new(touchin_excluded_files, '')
included_files = included_files.select { |file_name| not swift_files.is_excluded_file(file_name) } included_files = included_files.select { |file_name| not swift_files.is_excluded_file(file_name) }
included_files = included_files.map { |file_path| source_root_path + file_path } included_files = included_files.map { |file_path| source_root_path + file_path }
touchin_swiftlint_yaml_manager.update('excluded', []) touchin_swiftlint_yaml_manager.update('excluded', [])
touchin_swiftlint_yaml_manager.update('included', included_files) touchin_swiftlint_yaml_manager.update('included', included_files)
if not included_files.nilOrEmpty? if not included_files.nilOrEmpty?
run_single_strategy(@touchin_swiftlint_yaml_temporary_path) run_single_strategy(@touchin_swiftlint_yaml_temporary_path)
else else
puts 'Git found the swift files to check, but they are excluded in yaml' puts 'Git found the swift files to check, but they are excluded in yaml'
end end
end end
private private
def run_single_strategy(swiftlint_yaml_path) def run_single_strategy(swiftlint_yaml_path)
result_swiftlint_command = get_swiftlint_command(swiftlint_yaml_path) result_swiftlint_command = get_swiftlint_command(swiftlint_yaml_path)
puts result_swiftlint_command puts result_swiftlint_command
run_bash_command(result_swiftlint_command) run_bash_command(result_swiftlint_command)
end end
def run_multiple_strategy(touchin_swiftlint_yaml_temporary_path, old_swiftlint_yaml_temporary_path) def run_multiple_strategy(touchin_swiftlint_yaml_temporary_path, old_swiftlint_yaml_temporary_path)
touchin_swiftlint_command = get_swiftlint_command(touchin_swiftlint_yaml_temporary_path) touchin_swiftlint_command = get_swiftlint_command(touchin_swiftlint_yaml_temporary_path)
old_swiftlint_command = get_swiftlint_command(old_swiftlint_yaml_temporary_path) old_swiftlint_command = get_swiftlint_command(old_swiftlint_yaml_temporary_path)
@ -123,34 +122,34 @@ class StrategyMaker
puts result_swiftlint_command puts result_swiftlint_command
run_bash_command(result_swiftlint_command) run_bash_command(result_swiftlint_command)
end end
def get_swiftlint_command(swiftlint_yaml_path) def get_swiftlint_command(swiftlint_yaml_path)
autocorrect_command = @swiftlint_autocorrect_command + swiftlint_yaml_path autocorrect_command = @swiftlint_autocorrect_command + swiftlint_yaml_path
lint_command = @swiftlint_lint_command + swiftlint_yaml_path lint_command = @swiftlint_lint_command + swiftlint_yaml_path
return autocorrect_command + ' && ' + lint_command return autocorrect_command + ' && ' + lint_command
end end
def run_bash_command(bash_command) def run_bash_command(bash_command)
exit (exec bash_command) exit (exec bash_command)
end end
def create_yaml_managers_and_copy_temporary_files def create_yaml_managers_and_copy_temporary_files
create_copy_temporary_files create_copy_temporary_files
@touchin_swiftlint_yaml_manager = YamlManager.new(@touchin_swiftlint_yaml_temporary_path) @touchin_swiftlint_yaml_manager = YamlManager.new(@touchin_swiftlint_yaml_temporary_path)
@old_swiftlint_yaml_manager = YamlManager.new(@old_swiftlint_yaml_temporary_path) @old_swiftlint_yaml_manager = YamlManager.new(@old_swiftlint_yaml_temporary_path)
end end
def create_copy_temporary_files def create_copy_temporary_files
create_copy_temporary_touchin_files create_copy_temporary_touchin_files
FileUtils.cp @old_swiftlint_yaml_path, @old_swiftlint_yaml_temporary_path FileUtils.cp @old_swiftlint_yaml_path, @old_swiftlint_yaml_temporary_path
end end
def create_copy_temporary_touchin_files def create_copy_temporary_touchin_files
Dir.mkdir(@temporary_swiftlint_folder_name) unless Dir.exist?(@temporary_swiftlint_folder_name) Dir.mkdir(@temporary_swiftlint_folder_name) unless Dir.exist?(@temporary_swiftlint_folder_name)
FileUtils.cp @touchin_swiftlint_yaml_path, @touchin_swiftlint_yaml_temporary_path FileUtils.cp @touchin_swiftlint_yaml_path, @touchin_swiftlint_yaml_temporary_path
end end
def unique_exclude_files(touchin_swiftlint_yaml_manager, old_swiftlint_yaml_manager) def unique_exclude_files(touchin_swiftlint_yaml_manager, old_swiftlint_yaml_manager)
touchin_excluded_files = touchin_swiftlint_yaml_manager.get_configuration('excluded') touchin_excluded_files = touchin_swiftlint_yaml_manager.get_configuration('excluded')
old_excluded_files = old_swiftlint_yaml_manager.get_configuration('excluded') old_excluded_files = old_swiftlint_yaml_manager.get_configuration('excluded')

View File

@ -3,10 +3,7 @@ require_relative 'setting_option.rb'
require_relative 'strategy_maker.rb' require_relative 'strategy_maker.rb'
setting = SettingOption.new setting = SettingOption.new
strategy_maker = StrategyMaker.new(setting.project_root_path, strategy_maker = StrategyMaker.new(setting.project_root_path, setting.swiftlint_executable_path, setting.touchin_swiftlint_yaml_path)
setting.swiftlint_executable_path,
setting.touchin_swiftlint_yaml_path,
setting.old_swiftlint_yaml_path)
if setting.check_mode.eql? 'fully' and setting.use_multiple.true? if setting.check_mode.eql? 'fully' and setting.use_multiple.true?
strategy_maker.run_fully_multiple_strategy(setting.source_date) strategy_maker.run_fully_multiple_strategy(setting.source_date)

100
xcode/build_phases/swiftlint.sh Executable file → Normal file
View File

@ -1,98 +1,2 @@
#!/bin/sh SOURCES_DIR=${1:-${TARGET_NAME}} # first argument or TARGET_NAME
${PODS_ROOT}/SwiftLint/swiftlint autocorrect --path ${SOURCES_DIR} --config ${PROJECT_DIR}/build-scripts/xcode/.swiftlint.yml && ${PODS_ROOT}/SwiftLint/swiftlint --path ${SOURCES_DIR} --config ${PROJECT_DIR}/build-scripts/xcode/.swiftlint.yml
# Description:
# Runs swiftlint with selected or default config file.
# By default it runs only for modified files.
#
# Parameters:
# $1 - path to swiftlint executable.
# $2 - path to swiftlint config.
#
# Required environment variables:
# SCRIPT_DIR - directory of current script.
# SRCROOT - project directory.
#
# Optional environment variables:
# SWIFTLINT_EXECUTABLE - path to swiftlint executable.
# SWIFTLINT_CONFIG_PATH - path to swiftlint config.
# PODS_ROOT - cocoapods installation directory (eg. ${SRCROOT}/Pods) if SWIFTLINT_EXECUTABLE or ${1} is missing
# SCRIPT_INPUT_FILE_COUNT - number of files listed in "Input files" of build phase.
# SCRIPT_INPUT_FILE_{N} - file path to directory that should be checked.
# FORCE_LINT - don't exclude not modified files.
# AUTOCORRECT - format and fix code before lint.
#
# Example of usage:
# swiftlint.sh
# FORCE_LINT=true; swiftlint.sh
# swiftlint.sh Pods/Swiftlint/swiftlint build-scripts/xcode/.swiftlint.yml
#
readonly SOURCES_DIRS=`. ${SCRIPT_DIR}/common/read_input_file_names.sh "\n" ${SRCROOT}`
if [ -z "${SWIFTLINT_EXECUTABLE}" ]; then
if [ ! -z "${1}" ]; then
readonly SWIFTLINT_EXECUTABLE=${1}
elif [ ! -z "${PODS_ROOT}" ]; then
readonly SWIFTLINT_EXECUTABLE=${PODS_ROOT}/SwiftLint/swiftlint
else
readonly SWIFTLINT_EXECUTABLE=${SRCROOT}/Pods/SwiftLint/swiftlint
fi
fi
if [ -z "${SWIFTLINT_CONFIG_PATH}" ]; then
if [ ! -z "${2}" ]; then
readonly SWIFTLINT_CONFIG_PATH=${2}
else
readonly SWIFTLINT_CONFIG_PATH=${SCRIPT_DIR}/../.swiftlint.yml
fi
fi
if [ ! -z "${FORCE_LINT}" ]; then
# Если задана переменная FORCE_LINT, то проверяем все файлы проекта
for SOURCE_DIR in ${SOURCES_DIRS}; do
if [ ! -z "${AUTOCORRECT}" ]; then
${SWIFTLINT_EXECUTABLE} lint --config ${SWIFTLINT_CONFIG_PATH} --fix --format "${SRCROOT}/${SOURCE_DIR}"
fi
${SWIFTLINT_EXECUTABLE} lint --config ${SWIFTLINT_CONFIG_PATH} "${SRCROOT}/${SOURCE_DIR}"
done
else
# Xcode упадет, если будем использовать большое количество Script Input Files,
# так как просто переполнится стек - https://unix.stackexchange.com/questions/357843/setting-a-long-environment-variable-breaks-a-lot-of-commands
# Поэтому воспользуемся "скрытым" параметром Swiflint - https://github.com/realm/SwiftLint/pull/3313
# Создадим временный файл swiftlint_files с префиксом @ и в нем уже определим список файлов
# необходимых для линтовки :)
lint_files_path=`mktemp`
# Проходимся по папкам, которые требуют линтовки
for SOURCE_DIR in ${SOURCES_DIRS}; do
LINE_PREFIX="${SRCROOT}/"
pushd .
cd ${SRCROOT} # in case of runing script outside project folder (SPM)
# Отбираем файлы, которые были изменены или созданы
source_unstaged_files=$(git diff --diff-filter=d --name-only --line-prefix=${LINE_PREFIX} ${SOURCE_DIR} | grep "\.swift$")
source_staged_files=$(git diff --diff-filter=d --name-only --line-prefix=${LINE_PREFIX} --cached ${SOURCE_DIR} | grep "\.swift$")
popd
if [ ! -z "${source_unstaged_files}" ]; then
echo "${source_unstaged_files}" >> ${lint_files_path}
fi
if [ ! -z "${source_staged_files}" ]; then
echo "${source_staged_files}" >> ${lint_files_path}
fi
done
swiftlint_files_path="@${lint_files_path}"
if [ ! -z "${AUTOCORRECT}" ]; then
${SWIFTLINT_EXECUTABLE} lint --config ${SWIFTLINT_CONFIG_PATH} --fix --format --force-exclude --use-alternative-excluding ${swiftlint_files_path}
fi
${SWIFTLINT_EXECUTABLE} lint --config ${SWIFTLINT_CONFIG_PATH} --force-exclude --use-alternative-excluding ${swiftlint_files_path}
fi

View File

@ -1,5 +1,4 @@
readonly ARGUMENTS=("$@") arguments=("$@")
readonly IGNORED_FILES=$(IFS=, ; echo "${ARGUMENTS[*]}") ignored_files=$(IFS=, ; echo "${arguments[*]}")
readonly SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
ruby ${SCRIPT_DIR}/Unused.rb --config ${SCRIPT_DIR}/../UnusedConfig.yml --exclude ${IGNORED_FILES} ruby ${PROJECT_DIR}/build-scripts/xcode/build_phases/Unused.rb --config ${PROJECT_DIR}/build-scripts/xcode/UnusedConfig.yml --exclude ${ignored_files}

View File

@ -1,8 +0,0 @@
readonly SOURCES_DIR=${1:-${PROJECT_DIR}/${PRODUCT_NAME}} # first argument set product dir
readonly UNUSED_RESOURCES_SCRIPT=${2:-${PROJECT_DIR}/build-scripts/xcode/build_phases/common/unused_resources} # second argument set script path
readonly REPORTS_DIR=${PROJECT_DIR}/code-quality-reports
readonly FILES_TO_EXCLUDE=`find ${SOURCES_DIR} -type d -name Localization -or -name Generated | paste -sd " " -`
mkdir ${REPORTS_DIR}
${UNUSED_RESOURCES_SCRIPT} --project ${SOURCES_DIR} --exclude ${FILES_TO_EXCLUDE} --action "l" > ${REPORTS_DIR}/Unused_resources_log.txt

View File

@ -2,7 +2,6 @@ $appName = File.basename(Dir['../*.xcworkspace'].first, '.*')
require_relative 'fastlane/touchlane/lib/touchlane' require_relative 'fastlane/touchlane/lib/touchlane'
private_lane :installDependencies do |options| private_lane :installDependencies do |options|
podsReposPath = File.expand_path "~/.cocoapods/repos/master/" podsReposPath = File.expand_path "~/.cocoapods/repos/master/"
lockFilePath = "#{podsReposPath}/.git/index.lock" lockFilePath = "#{podsReposPath}/.git/index.lock"
@ -12,15 +11,49 @@ private_lane :installDependencies do |options|
sh("rm -rf #{podsReposPath}") sh("rm -rf #{podsReposPath}")
end end
if File.exists? "../Gemfile"
bundle_install(path: "../.gem")
end
cocoapods( cocoapods(
try_repo_update_on_error: true repo_update: true
) )
if File.exists? "../Cartfile"
use_rome = File.exists? "../Romefile"
swift_version = sh("xcrun swift --version | head -1 | sed 's/.*\\(\(.*\)\\).*/\\1/' | tr -d \"()\" | tr \" \" \"-\"").chop
rome_path = "Pods/Rome/rome"
rome_options = "--platform iOS --cache-prefix #{swift_version} --romefile Romefile"
carthage_install = lambda do
if use_rome
sh("cd .. && #{rome_path} download #{rome_options}")
end
carthage(command: "bootstrap", platform: "iOS", cache_builds: true)
if use_rome
sh("cd .. && #{rome_path} list --missing #{rome_options} | awk '{print $1}' | xargs -I framework_name #{rome_path} upload framework_name #{rome_options}")
end
end
begin
carthage_install.call
rescue
# workaround for https://github.com/Carthage/Carthage/issues/2298
sh("rm -rf ~/Library/Caches/org.carthage.CarthageKit")
carthage_install.call
end
end
end end
private_lane :uploadToFirebase do |options| private_lane :uploadToFirebase do |options|
releaseNotesFile = "release-notes.txt" releaseNotesFile = "release-notes.txt"
sh("touch ../#{releaseNotesFile}") sh("touch ../#{releaseNotesFile}")
sh("yarn install")
app_target_folder_name = options[:appName] || $appName app_target_folder_name = options[:appName] || $appName
configuration_type = Touchlane::ConfigurationType.from_type(options[:type]) configuration_type = Touchlane::ConfigurationType.from_type(options[:type])
@ -28,48 +61,33 @@ private_lane :uploadToFirebase do |options|
google_app_id = get_info_plist_value(path: gsp_plist_path, key: "GOOGLE_APP_ID") google_app_id = get_info_plist_value(path: gsp_plist_path, key: "GOOGLE_APP_ID")
firebase_app_distibution_groups_path = File.expand_path "../firebase_app_distribution_groups" firebase_app_distribution(
# Select groups_file or groups parameter depending on groups file existence
if File.exists? firebase_app_distibution_groups_path
firebase_app_distribution(
app: google_app_id,
ipa_path: options[:ipa_path],
groups_file: firebase_app_distibution_groups_path,
release_notes_file: releaseNotesFile
)
else
firebase_app_distribution(
app: google_app_id, app: google_app_id,
ipa_path: options[:ipa_path], ipa_path: options[:ipa_path],
groups: "touch-instinct", groups: "touch-instinct",
release_notes_file: releaseNotesFile release_notes_file: releaseNotesFile,
) firebase_cli_path: File.expand_path("../node_modules/firebase-tools/lib/bin/firebase.js")
end )
upload_symbols_to_crashlytics(
gsp_path: get_google_services_plist_path(app_target_folder_name, configuration_type)
)
end end
def upload_to_app_store_using_options(options, submit_for_review = false) private_lane :uploadToAppStore do |options|
upload_to_app_store( upload_to_app_store(
username: options[:username] || options[:apple_id], username: options[:username] || options[:apple_id],
api_key_path: options[:api_key_path],
api_key: options[:api_key],
ipa: options[:ipa_path], ipa: options[:ipa_path],
build_number: options[:ipa_path].nil? ? options[:buildNumber] : nil,
skip_binary_upload: options[:ipa_path].nil?,
skip_screenshots: true,
force: true, # skip metainfo prompt force: true, # skip metainfo prompt
submit_for_review: submit_for_review,
submission_information: options[:submission_information],
skip_metadata: true, skip_metadata: true,
team_id: options[:itc_team_id], team_id: options[:itc_team_id],
dev_portal_team_id: options[:team_id], dev_portal_team_id: options[:team_id]
precheck_include_in_app_purchases: false
) )
end end
private_lane :addShield do |options| private_lane :addShield do |options|
buildNumber = options[:buildNumber] buildNumber = options[:buildNumber]
buildDescription = options[:lane_name] # EnterpriseCustomerDev1WithoutSSLPinningRelease buildDescription = options[:xcconfig_name] # EnterpriseCustomerDev1WithoutSSLPinningRelease
.split(/(?=[A-Z])/) # -> ["Enterprise", "Customer", "Dev1", "Without", "S", "S", "L", "Pinning", "Release"] .split(/(?=[A-Z])/) # -> ["Enterprise", "Customer", "Dev1", "Without", "S", "S", "L", "Pinning", "Release"]
.map { |v| v.gsub(/[[:lower:]]+/, "") }[1..2] # -> ["E", "C", "D1", "W", "S", "S", "L", "P", "R"] -> ["C", "D1"] .map { |v| v.gsub(/[[:lower:]]+/, "") }[1..2] # -> ["E", "C", "D1", "W", "S", "S", "L", "P", "R"] -> ["C", "D1"]
.join # -> "CD1" .join # -> "CD1"
@ -85,139 +103,148 @@ private_lane :addShield do |options|
end end
private_lane :buildConfiguration do |options| private_lane :buildConfiguration do |options|
options[:appName] = options[:appName] || $appName appName = options[:appName] || $appName
lane_name = options[:lane_name] || lane_context[SharedValues::LANE_NAME] lane_name = lane_context[SharedValues::LANE_NAME]
options[:scheme] = options[:scheme] || options[:appName] options[:scheme] = appName
options[:lane_name] = lane_name options[:xcconfig_name] = lane_name
ipa_name = "#{options[:appName]}.ipa"
options[:output_name] = ipa_name
options[:ipa_path] = "./#{ipa_name}"
options[:dsym_path] = "./#{options[:appName]}.app.dSYM.zip"
options[:xcodeproj_path] = options[:xcodeproj_path] || "../#{options[:appName]}.xcodeproj"
options[:workspace] = options[:workspace] || File.expand_path("../#{options[:appName]}.xcworkspace")
configuration_type = Touchlane::ConfigurationType.from_lane_name(lane_name) configuration_type = Touchlane::ConfigurationType.from_lane_name(lane_name)
options = fill_up_options_using_configuration_type(options, configuration_type, true) options = fill_up_options_using_configuration_type(options, configuration_type)
generate_xcodeproj_if_needed(options)
openKeychain(options) openKeychain(options)
if !options[:buildNumber].nil? if is_ci
increment_build_number( increment_build_number(
build_number: options[:buildNumber] build_number: options[:buildNumber]
) )
end end
installDependencies(options) ipa_name = "#{appName}.ipa"
options[:output_name] = ipa_name
run_code_generation_phase_if_needed(options) options[:ipa_path] = "./#{ipa_name}"
options[:dsym_path] = "./#{appName}.app.dSYM.zip"
options[:xcodeproj_path] = "../#{appName}.xcodeproj"
options[:workspace] = "./#{appName}.xcworkspace"
installDependencies(options)
if !(options[:uploadToFabric] || options[:uploadToAppStore]) if !(options[:uploadToFabric] || options[:uploadToAppStore])
options[:skip_package_ipa] = true options[:skip_package_ipa] = true
install_signing_identities(options) sync_code_signing_using_options(options)
buildArchive(options) # check build failures and static analysis buildArchive(options) # check build failures and static analysis
end end
if options[:uploadToFabric] if options[:uploadToFabric]
install_signing_identities(options) sync_code_signing_using_options(options)
addShield(options) addShield(options)
buildArchive(options) buildArchive(options)
uploadToFirebase(options) uploadToFirebase(options)
end end
if options[:uploadToAppStore] if options[:uploadToAppStore]
options[:compileBitcode] = options[:compileBitcode].nil? ? true : options[:compileBitcode]
options[:include_symbols] = options[:include_symbols].nil? ? true : options[:include_symbols] options[:include_symbols] = options[:include_symbols].nil? ? true : options[:include_symbols]
install_signing_identities(options) sync_code_signing_using_options(options)
buildArchive(options)
upload_to_app_store_using_options(options, false)
end
upload_symbols_to_crashlytics( buildArchive(options)
gsp_path: get_google_services_plist_path(options[:appName], configuration_type) uploadToAppStore(options)
) end
end end
private_lane :buildArchive do |options| private_lane :buildArchive do |options|
require 'json'
icloudEnvironment = options[:iCloudContainerEnvironment] || "" icloudEnvironment = options[:iCloudContainerEnvironment] || ""
exportOptions = icloudEnvironment.to_s.empty? ? {} : {iCloudContainerEnvironment: icloudEnvironment} exportOptions = icloudEnvironment.to_s.empty? ? {} : {iCloudContainerEnvironment: icloudEnvironment}
exportOptions[:compileBitcode] = options[:compileBitcode] || false
lane_name = options[:lane_name] xcconfig_name = options[:xcconfig_name]
configuration = options[:configuration] configuration = options[:configuration]
xcodeproj_path = options[:xcodeproj_path] xcodeproj_path = options[:xcodeproj_path]
xcodes(select_for_current_build_only: true)
if configuration != "AppStore" # AppStore uses xcconfig choosen in Xcode if configuration != "AppStore" # AppStore uses xcconfig choosen in Xcode
set_xcconfig_for_configuration_of_project(lane_name, configuration, xcodeproj_path) set_xcconfig_for_configuration_of_project(xcconfig_name, configuration, xcodeproj_path)
end end
gym( gym(
clean: true, clean: true,
workspace: options[:workspace], workspace: options[:workspace],
scheme: options[:scheme], scheme: options[:scheme],
archive_path: "./#{$appName}.xcarchive", archive_path: "./",
buildlog_path: "./", output_directory: "./",
output_name: options[:output_name], output_name: options[:output_name],
configuration: configuration, configuration: configuration,
export_method: options[:export_method], export_method: options[:export_method],
export_options: exportOptions, export_options: exportOptions,
skip_package_ipa: options[:skip_package_ipa], skip_package_ipa: options[:skip_package_ipa],
include_symbols: options[:include_symbols] || false include_symbols: options[:include_symbols] || false,
include_bitcode: options[:compileBitcode] || false,
) )
end end
lane :SubmitForReview do |options| lane :CreatePushCertificate do |options|
configuration_type = Touchlane::ConfigurationType.from_type("appstore") configuration = get_configuration_for_type(options[:type] || "development")
options = fill_up_options_using_configuration_type(options, configuration_type, false) options = configuration.to_options.merge(options)
upload_to_app_store_using_options(options, true) certificates_path = File.expand_path "../Certificates"
Dir.mkdir(certificates_path) unless File.directory?(certificates_path)
app_identifier = options[:app_identifier]
get_push_certificate(
development: options[:development].nil? ? true : options[:development],
generate_p12: true,
active_days_limit: 30, # create new certificate if old one will expire in 30 days
save_private_key: false,
app_identifier: (app_identifier.is_a? Array) ? app_identifier.first : app_identifier,
username: options[:username] || options[:apple_id],
team_id: options[:team_id],
p12_password: "123", # empty password won't work with Pusher
output_path: certificates_path
)
end end
lane :InstallDevelopmentSigningIdentities do |options| lane :SyncCodeSigning do |options|
configuration_type = Touchlane::ConfigurationType.from_type("development") configuration_type = Touchlane::ConfigurationType.from_type(options[:type])
options = fill_up_options_using_configuration_type(options, configuration_type) options = fill_up_options_using_configuration_type(options, configuration_type)
install_signing_identities(options) sync_code_signing_using_options(options)
end end
lane :RefreshProfiles do |options| lane :SyncSymbols do |options|
type = options[:type] || "development" configuration = get_configuration_for_type(options[:type])
options = configuration.to_options.merge(options)
configuration_type = Touchlane::ConfigurationType.from_type(type) appName = options[:appName] || $appName
options = fill_up_options_using_configuration_type(options, configuration_type)
refresh_profiles(options) xcodeproj_path = File.expand_path "../#{appName}.xcodeproj"
end
lane :ReplaceDevelopmentCertificate do |options| version_number = options[:version] || get_version_number(xcodeproj: xcodeproj_path, target: appName)
configuration_type = Touchlane::ConfigurationType.from_type("development") build_number = options[:build_number] || get_build_number(xcodeproj: xcodeproj_path)
options = fill_up_options_using_configuration_type(options, configuration_type, true)
replace_development_certificate(options) if configuration.type.is_app_store
end download_dsyms(
username: options[:username],
app_identifier: options[:app_identifier].first,
team_id: options[:itc_team_id],
version: version_number,
build_number: build_number
)
end
lane :SyncAppStoreIdentities do |options| app_target_folder_name = appName
configuration_type = Touchlane::ConfigurationType.from_type("appstore")
options = fill_up_options_using_configuration_type(options, configuration_type, true)
options[:readonly] = false upload_symbols_to_crashlytics(
sync_signing_identities(options) gsp_path: get_google_services_plist_path(app_target_folder_name, configuration.type)
end )
lane :ManuallyUpdateCodeSigning do |options| clean_build_artifacts
manually_update_code_signing(get_default_options.merge(options))
end end
private_lane :openKeychain do |options| private_lane :openKeychain do |options|
@ -231,7 +258,7 @@ private_lane :openKeychain do |options|
name: options[:keychain_name], name: options[:keychain_name],
password: options[:keychain_password], password: options[:keychain_password],
unlock: true, unlock: true,
timeout: 0, timeout: false,
add_to_search_list: !keychain_exists add_to_search_list: !keychain_exists
) )
else else
@ -242,78 +269,111 @@ private_lane :openKeychain do |options|
end end
end end
def get_default_options lane :ManuallyUpdateCodeSigning do |options|
{ # based on this article https://medium.com/@jonathancardoso/using-fastlane-match-with-existing-certificates-without-revoking-them-a325be69dac6
:git_url => get_signing_identities_path(), require 'fastlane_core'
:signing_identities_path => get_signing_identities_path(), require 'match'
:storage_mode => Touchlane::LocalStorage::STORAGE_TYPE
}
end
def get_signing_identities_path conf = FastlaneCore::Configuration.create(Match::Options.available_options, {})
File.expand_path "../EncryptedSigningIdentities" conf.load_configuration_file("Matchfile")
end
def fill_up_options_using_configuration_type(options, configuration_type, keychain_password_required = false) git_url = conf.config_file_options[:git_url]
configuration = get_configuration_for_type(configuration_type.type) shallow_clone = false
branch = 'fastlane_certificates'
api_key_path = File.expand_path "../fastlane/#{configuration_type.prefix}_api_key.json" storage_conf = lambda do
is_api_key_file_exists = File.exists?(api_key_path) 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
# default_options required to be empty due to the possibility of skipping the configuration type check below return new_storage
end
default_options = get_default_options
encryption_conf_for_storage = lambda do |stor|
# Check whether configuration type is required to configure one of api key parameters or not new_encryption = Match::Encryption.for_storage_mode('git', { git_url: git_url, working_directory: stor.working_directory})
new_encryption.decrypt_files
if configuration_type.is_app_store || configuration_type.is_development return new_encryption
end
# Check whether API key JSON file exists or not
get_all_files = lambda do |stor|
if is_api_key_file_exists Dir[File.join(stor.working_directory, "**", "*.{cer,p12,mobileprovision}")]
end
# If exists then fill in all required information through api_key_path parameter
# and set a value to an options` parameter respectively storage = storage_conf.call
encryption = encryption_conf_for_storage.call(storage)
default_options[:api_key_path] = api_key_path old_files = get_all_files.call(storage)
else
sh("open #{storage.working_directory}")
# If doesn't exist then build api_key parameter through app_store_connect_api_key action
# and set a value to an options` parameter respectively also # 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"
default_options[:api_key] = get_app_store_connect_api_key() STDIN.gets
end
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
default_options
.merge(configuration.to_options)
.merge(get_keychain_options(options, keychain_password_required))
.merge(options)
end end
def get_app_store_connect_api_key() def sync_code_signing_using_options(options)
require 'json' match(
app_identifier: options[:app_identifier],
api_key_parameters = JSON.parse(ENV['API_KEY_JSON']) username: options[:username] || options[:apple_id],
team_id: options[:team_id],
return app_store_connect_api_key( type: options[:type],
key_id: api_key_parameters['key_id'], readonly: options[:readonly].nil? ? true : options[:readonly],
issuer_id: api_key_parameters['issuer_id'], storage_mode: "git",
key_content: api_key_parameters['key'], git_url: options[:git_url],
duration: api_key_parameters['duration'], git_branch: "fastlane_certificates",
in_house: api_key_parameters['in_house'] shallow_clone: true,
clone_branch_directly: true,
keychain_name: options[:keychain_name],
keychain_password: options[:keychain_password],
skip_docs: true,
platform: "ios"
) )
end end
def get_keychain_options(options, keychain_password_required = false) def fill_up_options_using_configuration_type(options, configuration_type)
configuration = get_configuration_for_type(configuration_type.type)
configuration.to_options
.merge(get_keychain_options(options))
.merge(options)
end
def get_keychain_options(options)
keychain_name = options[:keychain_name] keychain_name = options[:keychain_name]
keychain_password = options[:keychain_password] keychain_password = options[:keychain_password]
if is_ci? if is_ci?
keychain_name = keychain_name || "ci.keychain" keychain_name = keychain_name || "ci.keychain"
keychain_password = keychain_password || "" keychain_password = keychain_password || ""
elsif keychain_password_required && keychain_password.nil? else
keychain_password = prompt( keychain_password = keychain_password || prompt(
text: "Please enter your keychain password (account password): ", text: "Please enter your keychain password (account password): ",
secure_text: true secure_text: true
) )
@ -329,10 +389,10 @@ def get_configuration_for_type(type)
end end
def get_google_services_plist_path(app_target_folder_name, configuration_type) def get_google_services_plist_path(app_target_folder_name, configuration_type)
File.expand_path "../#{app_target_folder_name}/Resources/GoogleService-Info.plist" File.expand_path "../#{app_target_folder_name}/Resources/#{configuration_type.prefix}-GoogleService-Info.plist"
end end
def set_xcconfig_for_configuration_of_project(lane_name, configuration, xcodeproj_path) def set_xcconfig_for_configuration_of_project(xcconfig_name, configuration, xcodeproj_path)
require 'xcodeproj' require 'xcodeproj'
project = Xcodeproj::Project.open(xcodeproj_path) project = Xcodeproj::Project.open(xcodeproj_path)
@ -340,8 +400,7 @@ def set_xcconfig_for_configuration_of_project(lane_name, configuration, xcodepro
target_to_modify_selector = lambda do |t| target_to_modify_selector = lambda do |t|
supported_product_types = [ supported_product_types = [
Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application], Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application],
Xcodeproj::Constants::PRODUCT_TYPE_UTI[:app_extension], Xcodeproj::Constants::PRODUCT_TYPE_UTI[:app_extension]
Xcodeproj::Constants::PRODUCT_TYPE_UTI[:framework]
] ]
return !t.test_target_type? && supported_product_types.include?(t.product_type) return !t.test_target_type? && supported_product_types.include?(t.product_type)
end end
@ -349,35 +408,12 @@ def set_xcconfig_for_configuration_of_project(lane_name, configuration, xcodepro
application_targets = project.native_targets.select(&target_to_modify_selector) application_targets = project.native_targets.select(&target_to_modify_selector)
application_targets.each do |target| application_targets.each do |target|
config_name = target.name + lane_name build_configuration = target.build_configuration_list[configuration]
config_name = target.name + xcconfig_name
build_configuration_reference = project.files.select { |f| f.path.start_with?(config_name) }.first build_configuration_reference = project.files.select { |f| f.path.start_with?(config_name) }.first
build_configuration.base_configuration_reference = build_configuration_reference
if !build_configuration_reference.nil? # target has custom xcconfig
build_configuration = target.build_configuration_list[configuration]
build_configuration.base_configuration_reference = build_configuration_reference
end
end end
project.save() project.save()
end end
def generate_xcodeproj_if_needed(options)
project_yml_path = File.expand_path "../project.yml"
if !File.exists?(options[:xcodeproj_path]) && File.exists?(project_yml_path)
xcodegen(
spec: project_yml_path
)
end
end
# Build phases
def run_code_generation_phase_if_needed(options)
code_generation_script_path = File.expand_path "../.githooks/scripts/CodeGen.sh"
if File.exists? code_generation_script_path
sh(code_generation_script_path, options[:workspace])
end
end

@ -1 +1 @@
Subproject commit a8f072f216684bd7f5563c0211f71d6637a5f92d Subproject commit 3f81529c1425a74f43fe84fe8befffd81a442cc7

View File

@ -1,132 +0,0 @@
require 'json'
require 'mustache'
require 'yaml'
require_relative '../fastlane/touchlane/lib/touchlane/configuration_type'
class String
def in_current_dir
"#{__dir__}/#{self}"
end
end
class ConfigRenderer
class XCConfigKeys
DEVELOPMENT_TEAM = "DEVELOPMENT_TEAM"
PRODUCT_BUNDLE_IDENTIFIER = "PRODUCT_BUNDLE_IDENTIFIER"
CODE_SIGN_STYLE = "CODE_SIGN_STYLE"
end
INHERITED_PREFIX = "$(inherited)"
private_constant :INHERITED_PREFIX
def initialize(configurations_file_path, build_parameters_path, configs_folder_name)
@configurations_file_path = configurations_file_path
@build_parameters_path = build_parameters_path
@configs_folder_name = configs_folder_name
end
def render_xconfigs
temp_configs_data_file_path = "configs_data.json".in_current_dir
generator_path = "build_options_helper/helper.py".in_current_dir
template_path = "target_xcconfig.mustache".in_current_dir
# Create config directory if needed
Dir.mkdir(@configs_folder_name) unless Dir.exist?(@configs_folder_name)
# Call python script and generate configs to config file
system("python #{generator_path} -bp #{@build_parameters_path} -o #{__dir__} -r ios_build_settings -p ios")
# Open settings, configurations and template files
target_xcconfig_tempate = File.read(template_path)
$configurations = YAML.load(File.open(@configurations_file_path))
$config_types = $configurations["types"]
targets = $configurations["targets"]
# Run through all target in project
targets.each do |target_name, target|
# Need open everytime, because script make some changes only for this target
configs = JSON.load(File.open(temp_configs_data_file_path))
# Run through all configs
configs.each do |config|
# Take default values
distribution_type = Touchlane::ConfigurationType.from_account_type(config["account_type"]).type
properties = target[distribution_type]
# Add properties from settings file
properties.each do |key, value|
if config["xcconfig_options"].any? { |option| key == option["key"] }
config["xcconfig_options"].map! { |option| key == option["key"] ? merge_config_data(key, option["value"], value) : option }
else
config["xcconfig_options"].append(config_option(key, value))
end
end
# Add missing properties if needed
config["xcconfig_options"].concat(generate_missing_properties(target_name, properties, distribution_type))
# Create settings pack
config_data = {
"target_name": target_name,
"abstract_targets_prefix": target["abstract_targets_prefix"],
"configuration": config
}
# Create file for every setting in loop
File.open(@configs_folder_name + "/" + target_name + config["name"] + ".xcconfig", 'w') { |file|
file.puts(Mustache.render(target_xcconfig_tempate, config_data))
}
end
end
# Remove config file, it's trash
File.delete(temp_configs_data_file_path) if File.exist?(temp_configs_data_file_path)
end
# Make tuple of key and value become mustache template element
def config_option(key, value)
return { "key" => key, "value" => value }
end
def merge_config_data(key, config_value, settings_value)
if settings_value.start_with?(INHERITED_PREFIX)
new_value = settings_value.split(INHERITED_PREFIX).last
return config_option(key, config_value + new_value)
else
return config_option(key, settings_value)
end
end
# Fetch development team from build configuration
def fetch_development_team(development_team_key, distribution_type)
current_config = $config_types[distribution_type]
team_value = current_config["team_id"]
return config_option(development_team_key, team_value)
end
# Generate missing properties if needed
def generate_missing_properties(target_name, properties, distribution_type)
result = []
# Bundle_id_key should be among the properties (required by fastlane)
unless properties.key?(XCConfigKeys::PRODUCT_BUNDLE_IDENTIFIER)
raise "#{target_name}: Could not find #{XCConfigKeys::PRODUCT_BUNDLE_IDENTIFIER} for #{distribution_type}"
end
unless properties.key?(XCConfigKeys::DEVELOPMENT_TEAM)
result.append(fetch_development_team(XCConfigKeys::DEVELOPMENT_TEAM, distribution_type))
end
unless properties.key?(XCConfigKeys::CODE_SIGN_STYLE)
result.append(config_option(XCConfigKeys::CODE_SIGN_STYLE, "Manual"))
end
return result
end
end

View File

@ -1,16 +1,13 @@
targets: targets:
TestProject: TestProject:
abstract_targets_prefix: "-TestProjectKit"
development: development:
PRODUCT_BUNDLE_IDENTIFIER: "ru.touchin.testproject" PRODUCT_BUNDLE_IDENTIFIER: "ru.touchin.testproject"
PROVISIONING_PROFILE_SPECIFIER: "TestProjectDev" PROVISIONING_PROFILE_SPECIFIER: "TestProjectDev"
CODE_SIGN_ENTITLEMENTS: "TestProject/Standard.entitlements" CODE_SIGN_ENTITLEMENTS: "TestProject/Standard.entitlements"
SWIFT_ACTIVE_COMPILATION_CONDITIONS: "$(inherited) DEBUG_MENU"
enterprise: enterprise:
PRODUCT_BUNDLE_IDENTIFIER: "com.touchin.testproject" PRODUCT_BUNDLE_IDENTIFIER: "com.touchin.testproject"
PROVISIONING_PROFILE_SPECIFIER: "TestProjectEnterprise" PROVISIONING_PROFILE_SPECIFIER: "TestProjectEnterprise"
CODE_SIGN_ENTITLEMENTS: "TestProject/Enterprise.entitlements" CODE_SIGN_ENTITLEMENTS: "TestProject/Enterprise.entitlements"
SWIFT_ACTIVE_COMPILATION_CONDITIONS: "$(inherited) DEBUG_MENU"
appstore: appstore:
PRODUCT_BUNDLE_IDENTIFIER: "ru.customer.domain" PRODUCT_BUNDLE_IDENTIFIER: "ru.customer.domain"
PROVISIONING_PROFILE_SPECIFIER: "TestProjectAppStore" PROVISIONING_PROFILE_SPECIFIER: "TestProjectAppStore"
@ -18,13 +15,13 @@ targets:
types: types:
development: development:
apple_id: "iosdev@touchin.ru" apple_id: "apple@touchin.ru"
team_id: "**********" team_id: "**********"
itc_team_id: "**********" itc_team_id: "**********"
enterprise: enterprise:
apple_id: "enterpriseapple@touchin.ru" apple_id: "enterpriseapple@touchin.ru"
team_id: "**********" team_id: "**********"
appstore: appstore:
apple_id: "iosdev@touchin.ru" apple_id: "apple@touchin.ru"
team_id: "**********" team_id: "**********"
itc_team_id: "**********" itc_team_id: "**********"

View File

@ -1,4 +1,7 @@
require_relative "config_renderer" require 'json'
require 'mustache'
require 'yaml'
# #
# Usage: render_xcconfigs.rb <configurations.yaml> <build_parameters.yaml> [<ouptut folder>] # Usage: render_xcconfigs.rb <configurations.yaml> <build_parameters.yaml> [<ouptut folder>]
# #
@ -7,9 +10,148 @@ require_relative "config_renderer"
# It is recommended to remove old .xcconfig files before running this script. # It is recommended to remove old .xcconfig files before running this script.
# #
class String
def in_current_dir
"#{__dir__}/#{self}"
end
end
# Input files paths # Input files paths
configurations_file_path = ARGV[0] configurations_file_path = ARGV[0]
temp_configs_data_file_path = "configs_data.json".in_current_dir
generator_path = "build_options_helper/helper.py".in_current_dir
template_path = "target_xcconfig.mustache".in_current_dir
build_parameters_path = ARGV[1] build_parameters_path = ARGV[1]
configs_folder_name = ARGV[2] || "TargetConfigurations" configs_folder_name = ARGV[2] || "TargetConfigurations"
ConfigRenderer.new(configurations_file_path, build_parameters_path, configs_folder_name).render_xconfigs() # Create config directory if needed
Dir.mkdir(configs_folder_name) unless Dir.exist?(configs_folder_name)
# Call python script and generate configs to config file
system("python #{generator_path} -bp #{build_parameters_path} -o #{__dir__} -r ios_build_settings -p ios")
# Open settings, configurations and template files
target_xcconfig_tempate = File.read(template_path)
$configurations = YAML.load(File.open(configurations_file_path))
$config_types = $configurations["types"]
# Set global property
targets = $configurations["targets"]
# Make tuple of key and value become mustache template element
def config_option(key, value)
return { "key" => key, "value" => value }
end
# Maps lane prefix to distribution type
def distribution_type_of(account_type)
case account_type
when "Standard"
"development"
when "Enterprise"
"enterprise"
when "AppStore"
"appstore"
else
raise "Error: Unsupported distribution type #{account_type}"
end
end
# Fetch development team from build configuration
def fetch_development_team(development_team_key, distribution_type)
current_config = $config_types[distribution_type]
team_value = current_config["team_id"]
return config_option(development_team_key, team_value)
end
# Return empty array or generated provisioning profile hash
def generate_provisioning_profile(provisioning_key, bundle_id, distribution_type)
case distribution_type
when "appstore"
app_store_profile = "match AppStore " + bundle_id
config_option(provisioning_key, app_store_profile)
else
config_option(provisioning_key, bundle_id)
end
end
def generate_google_service_info_plist_path(google_service_info_plist_key, target_name, distribution_type)
google_service_info_plist_path = target_name + "/Resources/"
path_suffix = case distribution_type
when "development"
"Standard-GoogleService-Info.plist"
when "enterprise"
"Enterprise-GoogleService-Info.plist"
else
"AppStore-GoogleService-Info.plist"
end
return config_option(google_service_info_plist_key, google_service_info_plist_path + path_suffix)
end
# Generate missing properties if needed
def generate_missing_properties(target_name, properties, distribution_type)
result = []
development_team_key = "DEVELOPMENT_TEAM"
provisioning_key = "PROVISIONING_PROFILE_SPECIFIER"
google_service_info_plist_key = "GOOGLE_SERVICE_INFO_PLIST_PATH"
bundle_id_key = "PRODUCT_BUNDLE_IDENTIFIER"
# Bundle_id_key should be among the properties (required by fastlane)
unless properties.key?(bundle_id_key)
raise "#{target_name}: Could not find #{bundle_id_key} for #{distribution_type}"
end
unless properties.key?(development_team_key)
result.append(fetch_development_team(development_team_key, distribution_type))
end
unless properties.key?(provisioning_key)
result.append(generate_provisioning_profile(provisioning_key, properties[bundle_id_key], distribution_type))
end
unless properties.key?(google_service_info_plist_key)
result.append(generate_google_service_info_plist_path(google_service_info_plist_key, target_name, distribution_type))
end
return result
end
# Run through all target in project
targets.each do |target_name, target|
# Need open everytime, because script make some changes only for this target
configs = JSON.load(File.open(temp_configs_data_file_path))
# Run through all configs
configs.each do |config|
# Take default values
distribution_type = distribution_type_of(config["account_type"])
properties = target[distribution_type]
# Add properties from settings file
properties.each do |key, value|
config["xcconfig_options"].append(config_option(key, value))
end
# Add missing properties if needed
config["xcconfig_options"].concat(generate_missing_properties(target_name, properties, distribution_type))
# Create settings pack
config_data = {
"target_name": target_name,
"configuration": config
}
# Create file for every setting in loop
File.open(configs_folder_name + "/" + target_name + config["name"] + ".xcconfig", 'w') { |file|
file.puts(Mustache.render(target_xcconfig_tempate, config_data))
}
end
end
# Remove config file, it's trash
File.delete(temp_configs_data_file_path) if File.exist?(temp_configs_data_file_path)

View File

@ -1,5 +1,7 @@
#include "Pods/Target Support Files/Pods{{abstract_targets_prefix}}-{{target_name}}/Pods{{abstract_targets_prefix}}-{{target_name}}.{{configuration.build_type}}.xcconfig" #include "Pods/Target Support Files/Pods-{{target_name}}/Pods-{{target_name}}.{{configuration.build_type}}.xcconfig"
{{#configuration.xcconfig_options}} {{#configuration.xcconfig_options}}
{{key}} = {{value}} {{key}} = {{value}}
{{/configuration.xcconfig_options}} {{/configuration.xcconfig_options}}
CODE_SIGN_STYLE = Manual

View File

@ -1,10 +1,6 @@
require "yaml" require "yaml"
module Touchlane module Touchlane
module SharedValues
TOUCH_BUNDLE_ID_PROFILE_NAME_MAPPING = :TOUCH_BUNDLE_ID_PROFILE_NAME_MAPPING
end
class Configuration class Configuration
def initialize(type, app_identifier, apple_id, team_id, itc_team_id) def initialize(type, app_identifier, apple_id, team_id, itc_team_id)
@type = type @type = type
@ -17,8 +13,6 @@ module Touchlane
attr_reader :type, :app_identifier, :apple_id, :team_id, :itc_team_id attr_reader :type, :app_identifier, :apple_id, :team_id, :itc_team_id
def self.from_file(path, type) def self.from_file(path, type)
Fastlane::Actions.lane_context[SharedValues::TOUCH_BUNDLE_ID_PROFILE_NAME_MAPPING] = {}
configuration_hash = load_configuration_from_file(path) configuration_hash = load_configuration_from_file(path)
attrs_hash = configuration_hash["types"][type] attrs_hash = configuration_hash["types"][type]
identifiers = get_app_identifiers_from_configuration_hash(configuration_hash, type) identifiers = get_app_identifiers_from_configuration_hash(configuration_hash, type)
@ -41,15 +35,8 @@ module Touchlane
def self.get_app_identifiers_from_configuration_hash(configuration_hash, type) def self.get_app_identifiers_from_configuration_hash(configuration_hash, type)
identifier_key = "PRODUCT_BUNDLE_IDENTIFIER" identifier_key = "PRODUCT_BUNDLE_IDENTIFIER"
profile_name_key = "PROVISIONING_PROFILE_SPECIFIER"
configuration_hash["targets"].collect do |target, types| configuration_hash["targets"].collect do |target, types|
bundle_id = types[type][identifier_key] types[type][identifier_key] or raise "#{target}: There is no #{identifier_key} field in #{type}"
profile_name = types[type][profile_name_key]
Fastlane::Actions.lane_context[SharedValues::TOUCH_BUNDLE_ID_PROFILE_NAME_MAPPING][bundle_id] = profile_name
bundle_id or raise "#{target}: There is no #{identifier_key} field in #{type}"
end end
end end

View File

@ -3,39 +3,28 @@ module Touchlane
DEVELOPMENT = "development" DEVELOPMENT = "development"
ENTERPRISE = "enterprise" ENTERPRISE = "enterprise"
APP_STORE = "appstore" APP_STORE = "appstore"
ADHOC = "adhoc"
DEVELOPMENT_PREFIX = "Standard" DEVELOPMENT_PREFIX = "Standard"
ENTERPRISE_PREFIX = "Enterprise" ENTERPRISE_PREFIX = "Enterprise"
APP_STORE_PREFIX = "AppStore" APP_STORE_PREFIX = "AppStore"
ADHOC_PREFIX = "AdHoc"
private_constant :DEVELOPMENT, :ENTERPRISE, :APP_STORE, :ADHOC_PREFIX private_constant :DEVELOPMENT, :ENTERPRISE, :APP_STORE
private_constant :DEVELOPMENT_PREFIX, :ENTERPRISE_PREFIX, :APP_STORE_PREFIX, :ADHOC_PREFIX private_constant :DEVELOPMENT_PREFIX, :ENTERPRISE_PREFIX, :APP_STORE_PREFIX
def initialize(type) def initialize(type)
@type = type @type = type
@is_app_store = type == APP_STORE
@is_development = type == DEVELOPMENT
case type case type
when DEVELOPMENT when DEVELOPMENT, ENTERPRISE
@export_method = type @export_method = type
@configuration = "Debug" @configuration = type == DEVELOPMENT ? "Debug" : "Release"
@prefix = DEVELOPMENT_PREFIX @is_app_store = false
when ENTERPRISE @prefix = type == DEVELOPMENT ? DEVELOPMENT_PREFIX : ENTERPRISE_PREFIX
@export_method = type
@configuration = "Release"
@prefix = ENTERPRISE_PREFIX
when APP_STORE when APP_STORE
@export_method = "app-store" @export_method = "app-store"
@configuration = "AppStore" @configuration = "AppStore"
@is_app_store = true
@prefix = APP_STORE_PREFIX @prefix = APP_STORE_PREFIX
when ADHOC
@export_method = type
@export_method = "ad-hoc"
@prefix = ADHOC_PREFIX
else else
raise "Unknown type passed #{type}" raise "Unknown type passed #{type}"
end end
@ -43,7 +32,7 @@ module Touchlane
private_class_method :new private_class_method :new
attr_reader :export_method, :type, :configuration, :is_app_store, :is_development, :prefix attr_reader :export_method, :type, :configuration, :is_app_store, :prefix
def self.from_lane_name(lane_name) def self.from_lane_name(lane_name)
case case
@ -53,11 +42,9 @@ module Touchlane
from_type(APP_STORE) from_type(APP_STORE)
when lane_name.start_with?(DEVELOPMENT_PREFIX) when lane_name.start_with?(DEVELOPMENT_PREFIX)
from_type(DEVELOPMENT) from_type(DEVELOPMENT)
when lane_name.start_with?(ADHOC_PREFIX)
from_type(ADHOC)
else else
raise "Unable to map #{lane_name} to #{ConfigurationType.class}." raise "Unable to map #{lane_name} to #{ConfigurationType.class}."
+ "Available prefixes: #{DEVELOPMENT_PREFIX}, #{ENTERPRISE_PREFIX}, #{APP_STORE_PREFIX}, #{ADHOC_PREFIX}" + "Available prefixes: #{DEVELOPMENT_PREFIX}, #{ENTERPRISE_PREFIX}, #{APP_STORE_PREFIX}"
end end
end end
@ -65,26 +52,9 @@ module Touchlane
new(type) new(type)
end end
def self.from_account_type(account_type)
case account_type
when DEVELOPMENT_PREFIX
from_type(DEVELOPMENT)
when ENTERPRISE_PREFIX
from_type(ENTERPRISE)
when APP_STORE_PREFIX
from_type(APP_STORE)
when ADHOC_PREFIX
from_type(ADHOC)
else
raise "Unable to map #{account_type} to #{ConfigurationType.class}."
+ "Available account types: #{DEVELOPMENT_PREFIX}, #{ENTERPRISE_PREFIX}, #{APP_STORE_PREFIX}, #{ADHOC_PREFIX}"
end
end
def to_options def to_options
{ {
:type => @type, :type => @type,
:development => @is_development,
:export_method => @export_method, :export_method => @export_method,
:configuration => @configuration :configuration => @configuration
} }

View File

@ -1,86 +0,0 @@
require 'match'
require 'fileutils'
require 'fastlane_core/ui/ui'
module Touchlane
class LocalStorage < Match::Storage::Interface
attr_accessor :signing_identities_path
STORAGE_TYPE = "local"
def self.configure(params)
return self.new(
# we can't pass signing_identities_path since params is hardcoded in match/runner.rb
signing_identities_path: params[:git_url]
)
end
def initialize(signing_identities_path: nil)
self.signing_identities_path = signing_identities_path
end
def prefixed_working_directory
return working_directory
end
def download
# Check if we already have a functional working_directory
return if @working_directory
# No existing working directory, creating a new one now
self.working_directory = Dir.mktmpdir
Dir.mkdir(self.signing_identities_path) unless File.exists?(self.signing_identities_path)
FileUtils.cp_r("#{self.signing_identities_path}/.", self.working_directory)
end
def human_readable_description
"Local folder [#{self.signing_identities_path}]"
end
def upload_files(files_to_upload: [], custom_message: nil)
# `files_to_upload` is an array of files that need to be moved to signing identities dir
# Those doesn't mean they're new, it might just be they're changed
# Either way, we'll upload them using the same technique
files_to_upload.each do |current_file|
# Go from
# "/var/folders/px/bz2kts9n69g8crgv4jpjh6b40000gn/T/d20181026-96528-1av4gge/profiles/development/Development_me.mobileprovision"
# to
# "profiles/development/Development_me.mobileprovision"
#
# We also have to remove the trailing `/` as Google Cloud doesn't handle it nicely
target_path = current_file.gsub(self.working_directory + "/", "")
absolute_target_path = File.join(self.signing_identities_path, target_path)
FileUtils.mkdir_p(File.dirname(absolute_target_path))
FileUtils.cp_r(current_file, absolute_target_path, remove_destination: true)
end
end
def delete_files(files_to_delete: [], custom_message: nil)
files_to_delete.each do |file_name|
target_path = file_name.gsub(self.working_directory + "/", "")
File.delete(File.join(self.signing_identities_path, target_path))
end
end
def skip_docs
false
end
def list_files(file_name: "", file_ext: "")
Dir[File.join(working_directory, "**", file_name, "*.#{file_ext}")]
end
def generate_matchfile_content
path = Fastlane::UI.input("Path to the signing identities folder: ")
return "git_url(\"#{path}\")"
end
end
end

View File

@ -1,16 +0,0 @@
require 'match'
# ugly hack to add support for custom storage
Match.module_eval do
def self.storage_modes
return ['git', 'google_cloud' 's3', Touchlane::LocalStorage::STORAGE_TYPE]
end
end
def register_local_storage_for_match
storage_type = Touchlane::LocalStorage::STORAGE_TYPE
Match::Storage.register_backend(type: storage_type, storage_class: Touchlane::LocalStorage)
Match::Encryption.register_backend(type: storage_type, encryption_class: Match::Encryption::OpenSSL)
end

View File

@ -1,9 +1,4 @@
module Touchlane module Touchlane
require_relative "touchlane/configuration_type" require_relative "configuration_type"
require_relative "touchlane/configuration" require_relative "configuration"
require_relative "match/storage/local_storage_register" end
require_relative "touchlane/actions/sync_signing_identities"
require_relative "touchlane/actions/refresh_profiles"
require_relative "touchlane/actions/replace_development_certificate"
require_relative "touchlane/actions/manually_update_code_signing"
end

View File

@ -1,62 +0,0 @@
require 'match'
require_relative '../../match/storage/local_storage'
def manually_update_code_signing(options)
register_local_storage_for_match()
storage_factory = lambda do
new_storage = Match::Storage.from_params(options)
new_storage.download
return new_storage
end
encryption_factory = lambda do |stor|
new_encryption = Match::Encryption.for_storage_mode(options[:storage_mode], { 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_factory.call
encryption = encryption_factory.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_factory.call
encryption = encryption_factory.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

View File

@ -1,96 +0,0 @@
require 'match'
require 'fastlane_core'
require 'Spaceship'
def refresh_profiles(options)
register_local_storage_for_match()
profiles_tmp_dir = Dir.mktmpdir
unless options[:cert_id]
cert_type = Match.cert_type_sym(options[:type])
storage = Match::Storage.from_params(options)
storage.download
output_dir_certs = File.join(storage.prefixed_working_directory, "certs", cert_type.to_s)
matched_certs = Dir.glob("*.cer", base: output_dir_certs)
if matched_certs.empty?
FastlaneCore::UI.error("Unable to locate certificate to upate profiles")
raise "No certificates found at #{output_dir_certs}"
end
if matched_certs.length > 1
options[:cert_id] = File.basename(FastlaneCore::UI.select("Please select the certificate", matched_certs), ".cer")
else
options[:cert_id] = File.basename(matched_certs.first, ".cer")
end
if options[:cert_path].nil? && options[:p12_path].nil?
encryption = Match::Encryption.for_storage_mode(options[:storage_mode], { working_directory: storage.working_directory })
encryption.decrypt_files
tmp_certs_dir = Dir.mktmpdir
cer_file_name = "#{options[:cert_id]}.cer"
cert_path = File.join(output_dir_certs, cer_file_name)
options[:cert_path] = File.join(tmp_certs_dir, cer_file_name)
p12_file_name = "#{options[:cert_id]}.p12"
p12_path = File.join(output_dir_certs, p12_file_name)
options[:p12_path] = File.join(tmp_certs_dir, p12_file_name)
IO.copy_stream(cert_path, options[:cert_path])
IO.copy_stream(p12_path, options[:p12_path])
end
end
app_identifier = options[:app_identifier]
Spaceship::ConnectAPI.token = Spaceship::ConnectAPI::Token.from_json_file(options[:api_key_path])
if app_identifier.is_a? Array
app_identifier.each { |app_id| refresh_profile_for_app(options, app_id, profiles_tmp_dir) }
else
refresh_profile_for_app(options, app_identifier, profiles_tmp_dir)
end
end
def refresh_profile_for_app(options, app_id, profiles_tmp_dir)
provisioning_name = Fastlane::Actions.lane_context[Touchlane::SharedValues::TOUCH_BUNDLE_ID_PROFILE_NAME_MAPPING][app_id]
profiles = Spaceship::ConnectAPI::Profile.all(filter: { name: provisioning_name })
if profiles.empty?
sigh_for_app(options, app_id, provisioning_name, profiles_tmp_dir)
else
FastlaneCore::UI.important("Did find existing profile #{provisioning_name}. Removing it from dev portal.")
profiles.each { |profile| profile.delete! if profile.name == provisioning_name }
sigh_for_app(options, app_id, provisioning_name, profiles_tmp_dir)
end
Match::Importer.new.import_cert(
options,
cert_path: options[:cert_path],
p12_path: options[:p12_path],
profile_path: lane_context[Fastlane::Actions::SharedValues::SIGH_PROFILE_PATH]
)
end
def sigh_for_app(options, app_id, provisioning_name, profiles_tmp_dir)
sigh(
app_identifier: app_id,
development: options[:development],
username: options[:username] || options[:apple_id],
api_key_path: options[:api_key_path],
api_key: options[:api_key],
team_id: options[:team_id],
provisioning_name: provisioning_name,
output_path: profiles_tmp_dir,
cert_id: options[:cert_id],
force: true # will also add all available devices to this profile
)
end

View File

@ -1,24 +0,0 @@
def replace_development_certificate(options)
register_local_storage_for_match()
certs_path_tmp_dir = Dir.mktmpdir
cert(
development: true,
username: options[:username] || options[:apple_id],
api_key_path: options[:api_key_path],
api_key: options[:api_key],
team_id: options[:team_id],
output_path: certs_path_tmp_dir,
keychain_password: options[:keychain_password]
)
options[:cert_id] = lane_context[Fastlane::Actions::SharedValues::CERT_CERTIFICATE_ID]
options[:cert_path] = lane_context[Fastlane::Actions::SharedValues::CERT_FILE_PATH]
options[:p12_path] = File.join(File.dirname(options[:cert_path]), "#{options[:cert_id]}.p12")
options[:readonly] = false
refresh_profiles(options)
sh("open #{certs_path_tmp_dir}")
end

View File

@ -1,26 +0,0 @@
def install_signing_identities(options)
readonly_options = options
readonly_options[:readonly] = true
sync_signing_identities(readonly_options)
end
def sync_signing_identities(options)
register_local_storage_for_match()
match(
app_identifier: options[:app_identifier],
username: options[:username] || options[:apple_id],
api_key_path: options[:api_key_path],
api_key: options[:api_key],
team_id: options[:team_id],
type: options[:type],
readonly: options[:readonly].nil? ? true : options[:readonly],
storage_mode: options[:storage_mode],
# we can't pass signing_identities_path as parameter name since params is hardcoded in match/runner.rb
git_url: options[:signing_identities_path] || options[:git_url],
skip_docs: true,
keychain_name: options[:keychain_name],
keychain_password: options[:keychain_password]
)
end