Compare commits
16 Commits
master
...
gradle8_to
| Author | SHA1 | Date |
|---|---|---|
|
|
1baa9ce618 | |
|
|
2ddf356387 | |
|
|
09d3294a5b | |
|
|
a8b167af0f | |
|
|
2ebdc7e33f | |
|
|
ab6a83aee0 | |
|
|
6df29f2101 | |
|
|
dca38422f3 | |
|
|
e3437f34c1 | |
|
|
a6dcd67a81 | |
|
|
5c46386ebf | |
|
|
3cc63ffe5e | |
|
|
ec358728eb | |
|
|
78024ea1a6 | |
|
|
5e7a39d14d | |
|
|
e175e66e57 |
25
README.md
25
README.md
|
|
@ -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. В открывшемся окне ввести название новой схемы и нажать `ОК`
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<application>
|
||||||
|
<component name="CodeStyleSchemeSettings">
|
||||||
|
<option name="CURRENT_SCHEME_NAME" value="TouchInstinct" />
|
||||||
|
</component>
|
||||||
|
</application>
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<state>
|
|
||||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -1,33 +1,33 @@
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-gradle-plugin`
|
id("java-gradle-plugin")
|
||||||
`kotlin-dsl`
|
id("org.gradle.kotlin.kotlin-dsl") version "4.1.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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()
|
||||||
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// android gradle plugin, required by custom plugin
|
// android gradle plugin, required by custom plugin
|
||||||
implementation("com.android.tools.build:gradle:4.0.1")
|
implementation("com.android.tools.build:gradle:7.1.3")
|
||||||
|
|
||||||
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.10.0")
|
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.10.0")
|
||||||
implementation("de.aaschmid:gradle-cpd-plugin:3.1")
|
implementation("de.aaschmid:gradle-cpd-plugin:3.3")
|
||||||
|
|
||||||
// kotlin plugin, required by custom plugin
|
// kotlin plugin, required by custom plugin
|
||||||
implementation(kotlin("gradle-plugin", embeddedKotlinVersion))
|
implementation(kotlin("gradle-plugin", "1.8.10"))
|
||||||
|
|
||||||
gradleKotlinDsl()
|
|
||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
}
|
}
|
||||||
|
|
||||||
val compileKotlin: KotlinCompile by tasks
|
val compileKotlin: KotlinCompile by tasks
|
||||||
compileKotlin.kotlinOptions {
|
compileKotlin.kotlinOptions {
|
||||||
jvmTarget = "1.8"
|
jvmTarget = "17"
|
||||||
}
|
}
|
||||||
|
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,13 @@ abstract class ApiGeneratorPlugin : Plugin<Project> {
|
||||||
val outputLanguage = extension.outputLanguage ?: throw IllegalStateException("Configure output language code for api generator plugin")
|
val outputLanguage = extension.outputLanguage ?: throw IllegalStateException("Configure output language code for api generator plugin")
|
||||||
|
|
||||||
javaexec {
|
javaexec {
|
||||||
main = "-jar"
|
mainClass.set("-jar")
|
||||||
workingDir = rootDir
|
workingDir = rootDir
|
||||||
args = listOfNotNull(
|
args = listOfNotNull(
|
||||||
|
"--add-opens",
|
||||||
|
"java.base/java.lang=ALL-UNNAMED",
|
||||||
|
"--add-opens",
|
||||||
|
"java.base/java.time=ALL-UNNAMED",
|
||||||
configurations.getByName("apiGenerator").asPath,
|
configurations.getByName("apiGenerator").asPath,
|
||||||
"generate-client-code",
|
"generate-client-code",
|
||||||
"--output-language",
|
"--output-language",
|
||||||
|
|
@ -68,7 +72,7 @@ abstract class ApiGeneratorPlugin : Plugin<Project> {
|
||||||
"--package-name",
|
"--package-name",
|
||||||
extension.outputPackageName,
|
extension.outputPackageName,
|
||||||
"--recreate_output_dirs",
|
"--recreate_output_dirs",
|
||||||
extension.recreateOutputDir.toString()
|
extension.recreateOutputDir.toString(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
package static_analysis.linters
|
package static_analysis.linters
|
||||||
|
|
||||||
import com.android.build.gradle.AppExtension
|
|
||||||
import com.android.build.gradle.AppPlugin
|
import com.android.build.gradle.AppPlugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.kotlin.dsl.findByType
|
|
||||||
import static_analysis.errors.AndroidLintError
|
import static_analysis.errors.AndroidLintError
|
||||||
import static_analysis.errors.StaticAnalysisError
|
import static_analysis.errors.StaticAnalysisError
|
||||||
import static_analysis.plugins.StaticAnalysisExtension
|
import static_analysis.plugins.StaticAnalysisExtension
|
||||||
|
|
@ -33,22 +31,10 @@ class AndroidLinter : Linter {
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|
||||||
override fun setupForProject(project: Project, extension: StaticAnalysisExtension) {
|
override fun setupForProject(project: Project, extension: StaticAnalysisExtension) {
|
||||||
project.beforeEvaluate {
|
// Make sure to set lint options manually in modules gradle file
|
||||||
subprojects
|
// Otherwise you will get java.io.FileNotFoundException
|
||||||
.mapNotNull { it.extensions.findByType<AppExtension>() }
|
|
||||||
.first()
|
// See issue: https://github.com/TouchInstinct/BuildScripts/issues/310
|
||||||
.lintOptions.apply {
|
|
||||||
isAbortOnError = false
|
|
||||||
isCheckAllWarnings = true
|
|
||||||
isWarningsAsErrors = false
|
|
||||||
xmlReport = true
|
|
||||||
htmlReport = false
|
|
||||||
isCheckDependencies = true
|
|
||||||
disable("MissingConstraints", "VectorRaster")
|
|
||||||
xmlOutput = getLintReportFile()
|
|
||||||
lintConfig = file("${extension.buildScriptDir}/static_analysis_configs/lint.xml")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTaskNames(project: Project, buildType: String?): List<String> {
|
override fun getTaskNames(project: Project, buildType: String?): List<String> {
|
||||||
|
|
@ -62,11 +48,14 @@ class AndroidLinter : Linter {
|
||||||
.mapNotNull { subproject: Project ->
|
.mapNotNull { subproject: Project ->
|
||||||
subproject
|
subproject
|
||||||
.tasks
|
.tasks
|
||||||
.find { task -> task.name.contains(buildType, ignoreCase = true) && task.name.contains("lint") }
|
.filter { task ->
|
||||||
?.path
|
task.name.equals("lint${buildType}", ignoreCase = true)
|
||||||
|
|| task.name.equals("copy${buildType}AndroidLintReports", ignoreCase = true)
|
||||||
|
}
|
||||||
|
.map { it.path }
|
||||||
}
|
}
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Project.getLintReportFile() = file("${rootProject.buildDir}/reports/lint-report.xml")
|
private fun Project.getLintReportFile() = file("${rootProject.buildDir}/reports/lint-report.xml")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ class CpdLinter : Linter {
|
||||||
}
|
}
|
||||||
tasks.withType<Cpd> {
|
tasks.withType<Cpd> {
|
||||||
reports.xml.required.set(true)
|
reports.xml.required.set(true)
|
||||||
reports.xml.destination = getCpdReportFile()
|
reports.xml.outputLocation.set(getCpdReportFile())
|
||||||
ignoreFailures = true
|
ignoreFailures = true
|
||||||
source = getSources(extension.excludes)
|
source = getSources(extension.excludes)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,24 @@ package static_analysis.linters
|
||||||
|
|
||||||
import io.gitlab.arturbosch.detekt.Detekt
|
import io.gitlab.arturbosch.detekt.Detekt
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.file.FileTree
|
||||||
import static_analysis.errors.DetektError
|
import static_analysis.errors.DetektError
|
||||||
import static_analysis.errors.StaticAnalysisError
|
import static_analysis.errors.StaticAnalysisError
|
||||||
import static_analysis.plugins.StaticAnalysisExtension
|
import static_analysis.plugins.StaticAnalysisExtension
|
||||||
import static_analysis.utils.getSources
|
import static_analysis.utils.getSources
|
||||||
|
import static_analysis.utils.runCommand
|
||||||
import static_analysis.utils.typedChildren
|
import static_analysis.utils.typedChildren
|
||||||
import static_analysis.utils.xmlParser
|
import static_analysis.utils.xmlParser
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class DetektLinter : Linter {
|
class DetektLinter : Linter {
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val TAG = "DetektLinter"
|
||||||
|
const val ONLY_DIFFS_FLAG = "only-diffs"
|
||||||
|
const val GET_GIT_DIFFS_COMMAND = "git diff --name-only --ignore-submodules"
|
||||||
|
}
|
||||||
|
|
||||||
override val name: String = "Detekt"
|
override val name: String = "Detekt"
|
||||||
|
|
||||||
override fun getErrors(project: Project): List<StaticAnalysisError> = xmlParser(project.getDetektReportFile())
|
override fun getErrors(project: Project): List<StaticAnalysisError> = xmlParser(project.getDetektReportFile())
|
||||||
|
|
@ -50,11 +59,39 @@ class DetektLinter : Linter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
source = getSources(extension.excludes)
|
val diffsBranch = properties[ONLY_DIFFS_FLAG] as? String
|
||||||
|
source = getSources(extension.excludes, diffsBranch, project)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getSources(excludes: String, diffsBranch: String?, project: Project): FileTree = when (diffsBranch) {
|
||||||
|
null -> project.getSources(excludes)
|
||||||
|
else -> getGitDiffFiles(excludes, diffsBranch, project)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getGitDiffFiles(excludes: String, diffsBranch: String, project: Project): FileTree {
|
||||||
|
val getGitDiffsCommand = if (diffsBranch.isEmpty()) {
|
||||||
|
GET_GIT_DIFFS_COMMAND
|
||||||
|
} else {
|
||||||
|
GET_GIT_DIFFS_COMMAND.plus(" --merge-base $diffsBranch")
|
||||||
|
}
|
||||||
|
|
||||||
|
val gitDiffs = getGitDiffsCommand.runCommand()
|
||||||
|
|
||||||
|
if (gitDiffs.isNullOrEmpty()) {
|
||||||
|
project.logger.error("$TAG: Diffs are empty or specified branch or commit does not exists")
|
||||||
|
return project.files().asFileTree
|
||||||
|
}
|
||||||
|
|
||||||
|
val diffFiles = gitDiffs.lines()
|
||||||
|
.map { File(it) }
|
||||||
|
.filter { (it.extension == "kt" || it.extension == "java") && !excludes.contains(it.path) }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
return project.files(diffFiles).asFileTree
|
||||||
|
}
|
||||||
|
|
||||||
override fun getTaskNames(project: Project, buildType: String?): List<String> = listOf(":detekt")
|
override fun getTaskNames(project: Project, buildType: String?): List<String> = listOf(":detekt")
|
||||||
|
|
||||||
private fun Project.getDetektReportFile() = file("${rootProject.buildDir}/reports/detekt.xml")
|
private fun Project.getDetektReportFile() = file("${rootProject.buildDir}/reports/detekt.xml")
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,23 @@ class StaticAnalysisAndroidPlugin : StaticAnalysisPlugin() {
|
||||||
buildVariant = applicationVariants.first { it.name.contains("Debug") }.name
|
buildVariant = applicationVariants.first { it.name.contains("Debug") }.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Task to run detekt checks.
|
||||||
|
*
|
||||||
|
* @param -Ponly-diffs <branch or commit> if specified, only files modified
|
||||||
|
* relative to this branch or commit will be checked. If specified without value
|
||||||
|
* then current uncommited changes will be checked. If not specified all source files will be checked.
|
||||||
|
* @see DetektLinter.getGitDiffFiles, 'git diff' for more info.
|
||||||
|
* */
|
||||||
|
project.tasks.register("detektAnalysis") {
|
||||||
|
val detektLinter = linters.find { it is DetektLinter }
|
||||||
|
?: throw IllegalStateException("DetektLinter not found")
|
||||||
|
|
||||||
|
setupStaticAnalysisTask(
|
||||||
|
linters = listOf(detektLinter),
|
||||||
|
buildVariant = applicationVariants.first { it.name.contains("Debug") }.name
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package static_analysis.utils
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
fun String.runCommand(
|
||||||
|
directoryToExecute: File? = null,
|
||||||
|
timeoutSec: Long = 30,
|
||||||
|
): String? {
|
||||||
|
return try {
|
||||||
|
val parts = this.split("\\s".toRegex())
|
||||||
|
val process = ProcessBuilder(*parts.toTypedArray())
|
||||||
|
.directory(directoryToExecute)
|
||||||
|
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||||
|
.redirectError(ProcessBuilder.Redirect.PIPE)
|
||||||
|
.start()
|
||||||
|
|
||||||
|
process.waitFor(timeoutSec, TimeUnit.SECONDS)
|
||||||
|
process.inputStream.bufferedReader().readText()
|
||||||
|
} catch(e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
android {
|
||||||
|
lint {
|
||||||
|
abortOnError false
|
||||||
|
checkAllWarnings true
|
||||||
|
warningsAsErrors false
|
||||||
|
checkDependencies true
|
||||||
|
htmlReport false
|
||||||
|
textReport false
|
||||||
|
xmlReport true
|
||||||
|
xmlOutput file("${rootProject.buildDir}/reports/lint-report.xml")
|
||||||
|
lintConfig file("${rootProject.ext["buildScriptsDir"]}/static_analysis_configs/lint.xml")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,16 +12,15 @@
|
||||||
#
|
#
|
||||||
# Example of usage:
|
# Example of usage:
|
||||||
# export_src.sh TestProject ios android backend
|
# export_src.sh TestProject ios android backend
|
||||||
# GIT_BRANCH="develop" ./export_src.sh TestProject ios web
|
# GIT_BRANCH="develop"; ./export_src.sh TestProject ios web
|
||||||
#
|
#
|
||||||
|
|
||||||
if [ -z "${GIT_BRANCH}" ]; then
|
if [ -z "${GIT_BRANCH}" ]; then
|
||||||
GIT_BRANCH="master"
|
GIT_BRANCH="master"
|
||||||
fi
|
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=$@
|
||||||
|
|
@ -30,18 +29,7 @@ clone_platform() {
|
||||||
PROJECT_NAME=$1
|
PROJECT_NAME=$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_NAME}-${PLATFORM}.git" --branch "${GIT_BRANCH}"
|
||||||
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}"
|
||||||
|
|
@ -55,17 +43,7 @@ do
|
||||||
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 -q ${SRC_FOLDER_NAME}.zip .
|
||||||
|
|
||||||
open .
|
open .
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@
|
||||||
<!--All activities should have locked orientation-->
|
<!--All activities should have locked orientation-->
|
||||||
<issue id="LockedOrientationActivity" severity="ignore" />
|
<issue id="LockedOrientationActivity" severity="ignore" />
|
||||||
|
|
||||||
|
<!-- TODO: Update Timber version. See this issue: https://github.com/JakeWharton/timber/issues/408 -->
|
||||||
|
<issue id="WrongTimberUsageDetector" severity="ignore" />
|
||||||
|
|
||||||
<issue id="AllowAllHostnameVerifier" severity="error" />
|
<issue id="AllowAllHostnameVerifier" severity="error" />
|
||||||
<issue id="InvalidUsesTagAttribute" severity="error" />
|
<issue id="InvalidUsesTagAttribute" severity="error" />
|
||||||
<issue id="MissingIntentFilterForMediaSearch" severity="error" />
|
<issue id="MissingIntentFilterForMediaSearch" severity="error" />
|
||||||
|
|
|
||||||
|
|
@ -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,8 @@ 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
|
- prefer_zero_over_explicit_init
|
||||||
- discouraged_assert
|
- fallthrough
|
||||||
- discouraged_none_name
|
|
||||||
- shorthand_optional_binding
|
|
||||||
|
|
||||||
# style
|
# style
|
||||||
|
|
||||||
|
|
@ -58,46 +55,29 @@ 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
|
- 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"
|
- "**/Generated"
|
||||||
- "**/Resources"
|
- "**/Resources"
|
||||||
- ".gem"
|
|
||||||
- "**/*.app"
|
|
||||||
|
|
||||||
line_length:
|
line_length:
|
||||||
warning: 128
|
warning: 128
|
||||||
|
|
@ -129,7 +109,6 @@ identifier_name:
|
||||||
- id
|
- id
|
||||||
- ok
|
- ok
|
||||||
- URL
|
- URL
|
||||||
- qr
|
|
||||||
- x
|
- x
|
||||||
- y
|
- y
|
||||||
- z
|
- z
|
||||||
|
|
@ -142,17 +121,30 @@ 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 +165,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: '[а-яА-Я]+'
|
||||||
|
|
@ -238,6 +237,12 @@ custom_rules:
|
||||||
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: warning
|
||||||
|
|
||||||
|
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: warning
|
||||||
|
|
||||||
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):'
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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/*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
PROJECT_DIR="${DIR}/../../../"
|
|
||||||
|
|
||||||
make init -C ${PROJECT_DIR}
|
|
||||||
|
|
@ -24,69 +24,39 @@
|
||||||
readonly EXIT_SUCCESS=0
|
readonly EXIT_SUCCESS=0
|
||||||
readonly EXIT_FAILURE=1
|
readonly EXIT_FAILURE=1
|
||||||
|
|
||||||
readonly TRUE=1
|
readonly TRUE=0
|
||||||
readonly FALSE=0
|
readonly FALSE=1
|
||||||
|
|
||||||
readonly LOG_TAG="API-GENERATOR"
|
readonly LOG_TAG="API-GENERATOR"
|
||||||
|
|
||||||
notice()
|
notice()
|
||||||
{
|
{
|
||||||
echo "${LOG_TAG}:NOTICE: ${1}" >&2
|
echo "${LOG_TAG}:NOTICE: ${1}"
|
||||||
}
|
}
|
||||||
|
|
||||||
debug()
|
debug()
|
||||||
{
|
{
|
||||||
if [ ! -z "${VERBOSE}" ]; then
|
if [ ! -z "${VERBOSE}" ]; then
|
||||||
echo "${LOG_TAG}:DEBUG: ${1}" >&2
|
echo "${LOG_TAG}:DEBUG: ${1}"
|
||||||
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
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
is_force_run()
|
is_force_run()
|
||||||
{
|
{
|
||||||
if [ -z "${FORCE_RUN}" ]; then
|
if [ -z "${FORCE_RUN}" ]; then
|
||||||
echo ${FALSE}
|
return ${FALSE}
|
||||||
return
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local -r STR_MODE=`tr "[:upper:]" "[:lower:]" <<< ${FORCE_RUN}`
|
local -r STR_MODE=`tr "[:upper:]" "[:lower:]" <<< ${FORCE_RUN}`
|
||||||
|
|
||||||
if [ ${STR_MODE} == "yes" ] || [ ${STR_MODE} == "true" ] || [ ${STR_MODE} == "1" ]; then
|
if [ ${STR_MODE} == "yes" ] || [ ${STR_MODE} == "true" ] || [ ${STR_MODE} == "1" ]; then
|
||||||
echo ${TRUE}
|
return ${TRUE}
|
||||||
else
|
|
||||||
echo ${FALSE}
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
return ${FALSE}
|
||||||
}
|
}
|
||||||
|
|
||||||
is_single_file()
|
get_current_commit()
|
||||||
{
|
|
||||||
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 "${API_SPEC_DIR}" ]; then
|
||||||
if [ ! -z "${1}" ]; then
|
if [ ! -z "${1}" ]; then
|
||||||
|
|
@ -99,60 +69,11 @@ get_api_spec_current_commit()
|
||||||
fi
|
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()
|
is_nothing_changed_since_last_check()
|
||||||
{
|
{
|
||||||
if [ `is_force_run` -eq ${TRUE} ]; then
|
if is_force_run; then
|
||||||
notice "Force run detected. Skipping commits comparison."
|
notice "Force run detected. Skipping commits comparison."
|
||||||
echo ${TRUE}
|
return ${EXIT_FAILURE}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "${COMMIT_FILE_PATH}" ]; then
|
if [ -z "${COMMIT_FILE_PATH}" ]; then
|
||||||
|
|
@ -160,33 +81,24 @@ is_nothing_changed_since_last_check()
|
||||||
local -r COMMIT_FILE_PATH=${1}
|
local -r COMMIT_FILE_PATH=${1}
|
||||||
else
|
else
|
||||||
debug "COMMIT_FILE_PATH should be defined or passed as first argument!"
|
debug "COMMIT_FILE_PATH should be defined or passed as first argument!"
|
||||||
echo ${FALSE}
|
return ${EXIT_FAILURE}
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ `is_api_spec_under_source_control` -eq ${TRUE} ]; then
|
local -r CURRENT_COMMIT=`get_current_commit`
|
||||||
local -r CURRENT_COMMIT=`get_api_spec_current_commit`
|
|
||||||
|
|
||||||
local -r LAST_CHECKED_COMMIT=`cat ${COMMIT_FILE_PATH} 2> /dev/null || echo ""`
|
local -r LAST_CHECKED_COMMIT=`cat ${COMMIT_FILE_PATH} 2> /dev/null || echo ""`
|
||||||
|
|
||||||
if [ ${CURRENT_COMMIT} = "${LAST_CHECKED_COMMIT}" ]; then
|
if [ ${CURRENT_COMMIT} = "${LAST_CHECKED_COMMIT}" ]; then
|
||||||
if [ `is_api_spec_has_uncommited_changes` -eq ${TRUE} ]; then
|
return ${EXIT_SUCCESS}
|
||||||
notice "API spec has uncomitted changes."
|
|
||||||
echo ${FALSE}
|
|
||||||
else
|
|
||||||
echo ${TRUE}
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo ${FALSE}
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo ${FALSE}
|
return ${EXIT_FAILURE}
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
record_current_commit()
|
record_current_commit()
|
||||||
{
|
{
|
||||||
if [ `is_force_run` -eq ${TRUE} ]; then
|
if is_force_run; then
|
||||||
notice "Force run detected. Commit won't be recorder."
|
notice "Force run detected. Commit won't be recorder."
|
||||||
exit ${EXIT_SUCCESS}
|
exit ${EXIT_SUCCESS}
|
||||||
fi
|
fi
|
||||||
|
|
@ -200,7 +112,7 @@ record_current_commit()
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local -r CURRENT_COMMIT=`get_api_spec_current_commit`
|
local -r CURRENT_COMMIT=`get_current_commit`
|
||||||
|
|
||||||
echo ${CURRENT_COMMIT} > ${COMMIT_FILE_PATH}
|
echo ${CURRENT_COMMIT} > ${COMMIT_FILE_PATH}
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +152,7 @@ openapi_codegen()
|
||||||
|
|
||||||
notice "OpenAPI spec generation for ${OPEN_API_SPEC_PATH}"
|
notice "OpenAPI spec generation for ${OPEN_API_SPEC_PATH}"
|
||||||
|
|
||||||
local -r CODEGEN_VERSION="3.0.34"
|
local -r CODEGEN_VERSION="3.0.33"
|
||||||
|
|
||||||
local -r CODEGEN_FILE_NAME="swagger-codegen-cli-${CODEGEN_VERSION}.jar"
|
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}"
|
local -r CODEGEN_DOWNLOAD_URL="https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/${CODEGEN_VERSION}/${CODEGEN_FILE_NAME}"
|
||||||
|
|
@ -255,9 +167,7 @@ openapi_codegen()
|
||||||
|
|
||||||
rm -rf ${OUTPUT_PATH}/${API_NAME} # remove previously generated API (if exists)
|
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}"
|
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
|
# flatten folders hierarchy
|
||||||
|
|
||||||
|
|
@ -302,9 +212,7 @@ api_generator_codegen()
|
||||||
|
|
||||||
. build-scripts/xcode/aux_scripts/download_file.sh ${FILE_NAME} ${DOWNLOAD_URL}
|
. 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)"
|
java -Xmx6g -jar "Downloads/${FILE_NAME}" generate-client-code --output-language SWIFT --specification-path ${API_SPEC_DIR} --output-path ${OUTPUT_PATH} --single-file true
|
||||||
|
|
||||||
exit_on_failure "${API_GENERATOR_COMMAND}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly BUILD_PHASES_DIR=${SRCROOT}/build_phases
|
readonly BUILD_PHASES_DIR=${SRCROOT}/build_phases
|
||||||
|
|
@ -313,8 +221,8 @@ mkdir -p ${BUILD_PHASES_DIR}
|
||||||
|
|
||||||
readonly COMMIT_FILE_PATH=${BUILD_PHASES_DIR}/api-generator-commit
|
readonly COMMIT_FILE_PATH=${BUILD_PHASES_DIR}/api-generator-commit
|
||||||
|
|
||||||
if [ `is_nothing_changed_since_last_check` -eq ${TRUE} ]; then
|
if is_nothing_changed_since_last_check; then
|
||||||
notice "Nothing was changed. API generation skipped."
|
notice "Nothing was changed api generation skipped."
|
||||||
exit ${EXIT_SUCCESS}
|
exit ${EXIT_SUCCESS}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,13 +61,17 @@ if has_input_files && \
|
||||||
SCRIPT_INPUT_FILE_VARIABLE_NAME="SCRIPT_INPUT_FILE_${i}"
|
SCRIPT_INPUT_FILE_VARIABLE_NAME="SCRIPT_INPUT_FILE_${i}"
|
||||||
SHELL_VARIABLE="\${${SCRIPT_INPUT_FILE_VARIABLE_NAME}}"
|
SHELL_VARIABLE="\${${SCRIPT_INPUT_FILE_VARIABLE_NAME}}"
|
||||||
RESOLVED_FILE_NAME=`envsubst <<< ${SHELL_VARIABLE}`
|
RESOLVED_FILE_NAME=`envsubst <<< ${SHELL_VARIABLE}`
|
||||||
|
INPUT_FILE_NAMES=${INPUT_FILE_NAMES}${FILE_NAMES_SEPARATOR}${RESOLVED_FILE_NAME}
|
||||||
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
|
done
|
||||||
|
|
||||||
|
FILE_NAMES_SEPARATOR_LENGTH=`awk '{ print length; }' <<< "${FILE_NAMES_SEPARATOR}"`
|
||||||
|
|
||||||
|
if [ ${FILE_NAMES_SEPARATOR_LENGTH} -gt 0 ] && \
|
||||||
|
[ ! -z "${INPUT_FILE_NAMES}" ]; then
|
||||||
|
|
||||||
|
# remove separator prefix
|
||||||
|
INPUT_FILE_NAMES=`cut -c${FILE_NAMES_SEPARATOR_LENGTH}- <<< ${INPUT_FILE_NAMES}`
|
||||||
|
fi
|
||||||
elif has_input_file_lists; then
|
elif has_input_file_lists; then
|
||||||
for i in `seq 0 $((${SCRIPT_INPUT_FILE_LIST_COUNT}-1))`
|
for i in `seq 0 $((${SCRIPT_INPUT_FILE_LIST_COUNT}-1))`
|
||||||
do
|
do
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# $1 $2 $3 $n - folders to exclude from code checking.
|
# $1 $2 $3 $n - folders to exclude from code checking.
|
||||||
#
|
#
|
||||||
# Required environment variables:
|
# Required environment variables:
|
||||||
# SRCROOT - project directory.
|
# PROJECT_DIR - project directory.
|
||||||
# SCRIPT_DIR - directory of current script.
|
# SCRIPT_DIR - directory of current script.
|
||||||
#
|
#
|
||||||
# Optional environment variables:
|
# Optional environment variables:
|
||||||
|
|
@ -15,47 +15,43 @@
|
||||||
# SCRIPT_INPUT_FILE_{N} - file path to directory that should be checked.
|
# SCRIPT_INPUT_FILE_{N} - file path to directory that should be checked.
|
||||||
#
|
#
|
||||||
# Modified files:
|
# Modified files:
|
||||||
# ${SRCROOT}/code-quality-reports/CPDLog.txt - check report.
|
# ${PROJECT_DIR}/code-quality-reports/CPDLog.txt - check report.
|
||||||
#
|
#
|
||||||
# Example of usage:
|
# Example of usage:
|
||||||
# copy_paste_detection.sh Generated Localization Pods
|
# runner.sh copy_paste_detection.sh Generated Localization Pods
|
||||||
#
|
#
|
||||||
|
|
||||||
EXIT_SUCCESS=0
|
readonly EXIT_SUCCESS=0
|
||||||
EXIT_FAILURE=1
|
readonly EXIT_FAILURE=1
|
||||||
|
|
||||||
|
. ${SCRIPT_DIR}/../aux_scripts/install_env.sh pmd
|
||||||
|
|
||||||
if which pmd >/dev/null; then
|
if which pmd >/dev/null; then
|
||||||
REPORTS_DIR="${SRCROOT}/code-quality-reports"
|
readonly REPORTS_DIR="${PROJECT_DIR}/code-quality-reports"
|
||||||
|
|
||||||
SOURCES_DIRS=`. ${SCRIPT_DIR}/common/read_input_file_names.sh " " ${SRCROOT}`
|
readonly SOURCES_DIRS=`. ${SCRIPT_DIR}/common/read_input_file_names.sh " " ${PROJECT_DIR}`
|
||||||
|
|
||||||
COMMAND_LINE_ARGUMENTS=$@
|
readonly COMMAND_LINE_ARGUMENTS=$@
|
||||||
|
|
||||||
FOLDERS_TO_EXCLUDE=""
|
FOLDERS_TO_EXLUDE=""
|
||||||
|
|
||||||
for argument in ${COMMAND_LINE_ARGUMENTS}
|
for argument in ${COMMAND_LINE_ARGUMENTS}
|
||||||
do
|
do
|
||||||
FOLDERS_TO_EXCLUDE=${FOLDERS_TO_EXCLUDE}"-or -name ${argument} "
|
FOLDERS_TO_EXLUDE=${FOLDERS_TO_EXLUDE}"-or -name ${argument} "
|
||||||
done
|
done
|
||||||
|
|
||||||
FOLDERS_TO_EXCLUDE=`echo ${FOLDERS_TO_EXCLUDE} | cut -c5-` # remove first "-or"
|
FOLDERS_TO_EXLUDE=`echo ${FOLDERS_TO_EXLUDE} | cut -c5-` # remove first "-or"
|
||||||
|
|
||||||
FILES_TO_EXCLUDE=`find ${SRCROOT} -type d ${FOLDERS_TO_EXCLUDE} | paste -sd " " -`
|
readonly FILES_TO_EXCLUDE=`find ${PROJECT_DIR} -type d ${FOLDERS_TO_EXLUDE} | paste -sd " " -`
|
||||||
|
|
||||||
mkdir -p ${REPORTS_DIR}
|
mkdir -p ${REPORTS_DIR}
|
||||||
|
|
||||||
DIRS_ARGUMENTS=""
|
pmd cpd --files ${SOURCES_DIRS} --exclude ${FILES_TO_EXCLUDE} --minimum-tokens 50 --language swift --encoding UTF-8 --format net.sourceforge.pmd.cpd.XMLRenderer --failOnViolation true > ${REPORTS_DIR}/cpd-output.xml
|
||||||
|
|
||||||
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
|
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
|
# Make paths relative to PROJECT_DIR, so different developers won't rewrite entire file
|
||||||
SED_REPLACEMENT_STRING=$(echo ${SRCROOT} | sed "s/\//\\\\\//g")
|
readonly SED_REPLACEMENT_STRING=$(echo ${PROJECT_DIR} | 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
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
# Description:
|
# Description:
|
||||||
# Runs swiftlint with selected or default config file.
|
# Runs swiftlint with selected or default config file.
|
||||||
# By default it runs only for modified files.
|
|
||||||
#
|
#
|
||||||
# Parameters:
|
# Parameters:
|
||||||
# $1 - path to swiftlint executable.
|
# $1 - path to swiftlint executable.
|
||||||
|
|
@ -11,15 +10,14 @@
|
||||||
# Required environment variables:
|
# Required environment variables:
|
||||||
# SCRIPT_DIR - directory of current script.
|
# SCRIPT_DIR - directory of current script.
|
||||||
# SRCROOT - project directory.
|
# SRCROOT - project directory.
|
||||||
|
# PODS_ROOT - cocoapods installation directory (eg. ${SRCROOT}/Pods).
|
||||||
#
|
#
|
||||||
# Optional environment variables:
|
# Optional environment variables:
|
||||||
# SWIFTLINT_EXECUTABLE - path to swiftlint executable.
|
# SWIFTLINT_EXECUTABLE - path to swiftlint executable.
|
||||||
# SWIFTLINT_CONFIG_PATH - path to swiftlint config.
|
# 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_COUNT - number of files listed in "Input files" of build phase.
|
||||||
# SCRIPT_INPUT_FILE_{N} - file path to directory that should be checked.
|
# SCRIPT_INPUT_FILE_{N} - file path to directory that should be checked.
|
||||||
# FORCE_LINT - don't exclude not modified files.
|
# FORCE_LINT - lint all project.
|
||||||
# AUTOCORRECT - format and fix code before lint.
|
|
||||||
#
|
#
|
||||||
# Example of usage:
|
# Example of usage:
|
||||||
# swiftlint.sh
|
# swiftlint.sh
|
||||||
|
|
@ -32,10 +30,8 @@ readonly SOURCES_DIRS=`. ${SCRIPT_DIR}/common/read_input_file_names.sh "\n" ${SR
|
||||||
if [ -z "${SWIFTLINT_EXECUTABLE}" ]; then
|
if [ -z "${SWIFTLINT_EXECUTABLE}" ]; then
|
||||||
if [ ! -z "${1}" ]; then
|
if [ ! -z "${1}" ]; then
|
||||||
readonly SWIFTLINT_EXECUTABLE=${1}
|
readonly SWIFTLINT_EXECUTABLE=${1}
|
||||||
elif [ ! -z "${PODS_ROOT}" ]; then
|
|
||||||
readonly SWIFTLINT_EXECUTABLE=${PODS_ROOT}/SwiftLint/swiftlint
|
|
||||||
else
|
else
|
||||||
readonly SWIFTLINT_EXECUTABLE=${SRCROOT}/Pods/SwiftLint/swiftlint
|
readonly SWIFTLINT_EXECUTABLE=${PODS_ROOT}/SwiftLint/swiftlint
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -50,11 +46,8 @@ fi
|
||||||
if [ ! -z "${FORCE_LINT}" ]; then
|
if [ ! -z "${FORCE_LINT}" ]; then
|
||||||
# Если задана переменная FORCE_LINT, то проверяем все файлы проекта
|
# Если задана переменная FORCE_LINT, то проверяем все файлы проекта
|
||||||
for SOURCE_DIR in ${SOURCES_DIRS}; do
|
for SOURCE_DIR in ${SOURCES_DIRS}; do
|
||||||
if [ ! -z "${AUTOCORRECT}" ]; then
|
${SWIFTLINT_EXECUTABLE} autocorrect --path ${SOURCE_DIR} --config ${SWIFTLINT_CONFIG_PATH}
|
||||||
${SWIFTLINT_EXECUTABLE} lint --config ${SWIFTLINT_CONFIG_PATH} --fix --format "${SRCROOT}/${SOURCE_DIR}"
|
${SWIFTLINT_EXECUTABLE} --path ${SOURCE_DIR} --config ${SWIFTLINT_CONFIG_PATH}
|
||||||
fi
|
|
||||||
|
|
||||||
${SWIFTLINT_EXECUTABLE} lint --config ${SWIFTLINT_CONFIG_PATH} "${SRCROOT}/${SOURCE_DIR}"
|
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
# Xcode упадет, если будем использовать большое количество Script Input Files,
|
# Xcode упадет, если будем использовать большое количество Script Input Files,
|
||||||
|
|
@ -63,21 +56,19 @@ else
|
||||||
# Создадим временный файл swiftlint_files с префиксом @ и в нем уже определим список файлов
|
# Создадим временный файл swiftlint_files с префиксом @ и в нем уже определим список файлов
|
||||||
# необходимых для линтовки :)
|
# необходимых для линтовки :)
|
||||||
|
|
||||||
lint_files_path=`mktemp`
|
lint_files_path="${SRCROOT}/build_phases/swiftlint_files"
|
||||||
|
|
||||||
|
# Если файл существует, то просто его очистим, если нет - создадим
|
||||||
|
> ${lint_files_path}
|
||||||
|
|
||||||
# Проходимся по папкам, которые требуют линтовки
|
# Проходимся по папкам, которые требуют линтовки
|
||||||
for SOURCE_DIR in ${SOURCES_DIRS}; do
|
for SOURCE_DIR in ${SOURCES_DIRS}; do
|
||||||
LINE_PREFIX="${SRCROOT}/"
|
# Путь к папке репозитория
|
||||||
|
path_prefix="`git rev-parse --show-toplevel`/"
|
||||||
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_unstaged_files=$(git diff --diff-filter=d --name-only --line-prefix=${path_prefix} ${SOURCE_DIR} | grep "\.swift$")
|
||||||
source_staged_files=$(git diff --diff-filter=d --name-only --line-prefix=${LINE_PREFIX} --cached ${SOURCE_DIR} | grep "\.swift$")
|
source_staged_files=$(git diff --diff-filter=d --name-only --line-prefix=${path_prefix} --cached ${SOURCE_DIR} | grep "\.swift$")
|
||||||
|
|
||||||
popd
|
|
||||||
|
|
||||||
if [ ! -z "${source_unstaged_files}" ]; then
|
if [ ! -z "${source_unstaged_files}" ]; then
|
||||||
echo "${source_unstaged_files}" >> ${lint_files_path}
|
echo "${source_unstaged_files}" >> ${lint_files_path}
|
||||||
|
|
@ -90,9 +81,6 @@ else
|
||||||
|
|
||||||
swiftlint_files_path="@${lint_files_path}"
|
swiftlint_files_path="@${lint_files_path}"
|
||||||
|
|
||||||
if [ ! -z "${AUTOCORRECT}" ]; then
|
${SWIFTLINT_EXECUTABLE} autocorrect --path ${swiftlint_files_path} --config ${SWIFTLINT_CONFIG_PATH} --force-exclude --use-alternative-excluding
|
||||||
${SWIFTLINT_EXECUTABLE} lint --config ${SWIFTLINT_CONFIG_PATH} --fix --format --force-exclude --use-alternative-excluding ${swiftlint_files_path}
|
${SWIFTLINT_EXECUTABLE} --path ${swiftlint_files_path} --config ${SWIFTLINT_CONFIG_PATH} --force-exclude --use-alternative-excluding
|
||||||
fi
|
|
||||||
|
|
||||||
${SWIFTLINT_EXECUTABLE} lint --config ${SWIFTLINT_CONFIG_PATH} --force-exclude --use-alternative-excluding ${swiftlint_files_path}
|
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
$appName = File.basename(Dir['../*.xcworkspace'].first, '.*')
|
$appName = File.basename(Dir['../*.xcworkspace'].first, '.*')
|
||||||
|
|
||||||
require_relative 'fastlane/touchlane/lib/touchlane'
|
require_relative 'fastlane/touchlane/lib/touchlane'
|
||||||
|
require_relative 'managers/managers'
|
||||||
|
|
||||||
|
# ugly hack to add support for custom storage
|
||||||
|
|
||||||
|
Match.module_eval do
|
||||||
|
def self.storage_modes
|
||||||
|
return %w(git google_cloud s3 local)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private_lane :installDependencies do |options|
|
private_lane :installDependencies do |options|
|
||||||
podsReposPath = File.expand_path "~/.cocoapods/repos/master/"
|
podsReposPath = File.expand_path "~/.cocoapods/repos/master/"
|
||||||
|
|
@ -13,7 +21,7 @@ private_lane :installDependencies do |options|
|
||||||
end
|
end
|
||||||
|
|
||||||
cocoapods(
|
cocoapods(
|
||||||
try_repo_update_on_error: true
|
repo_update: true
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -46,20 +54,18 @@ private_lane :uploadToFirebase do |options|
|
||||||
release_notes_file: releaseNotesFile
|
release_notes_file: releaseNotesFile
|
||||||
)
|
)
|
||||||
end
|
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)
|
def upload_to_app_store_using_options(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_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],
|
||||||
|
|
@ -102,7 +108,7 @@ private_lane :buildConfiguration do |options|
|
||||||
options[:workspace] = options[:workspace] || File.expand_path("../#{options[:appName]}.xcworkspace")
|
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)
|
generate_xcodeproj_if_needed(options)
|
||||||
|
|
||||||
|
|
@ -117,33 +123,33 @@ private_lane :buildConfiguration do |options|
|
||||||
installDependencies(options)
|
installDependencies(options)
|
||||||
|
|
||||||
run_code_generation_phase_if_needed(options)
|
run_code_generation_phase_if_needed(options)
|
||||||
|
generate_enabled_features_extension_if_needed(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)
|
upload_to_app_store_using_options(options)
|
||||||
)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private_lane :buildArchive do |options|
|
private_lane :buildArchive do |options|
|
||||||
|
|
@ -152,72 +158,103 @@ private_lane :buildArchive do |options|
|
||||||
|
|
||||||
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]
|
lane_name = options[:lane_name]
|
||||||
configuration = options[:configuration]
|
configuration = options[:configuration]
|
||||||
xcodeproj_path = options[:xcodeproj_path]
|
xcodeproj_path = options[:xcodeproj_path]
|
||||||
|
xcode_version = options[:xcodeVersion]
|
||||||
|
|
||||||
xcodes(select_for_current_build_only: true)
|
cmd = 'system_profiler -json SPDeveloperToolsDataType'
|
||||||
|
cmd_result = `#{cmd}`
|
||||||
|
spdeveloperToolsDataType = JSON.parse(cmd_result)['SPDeveloperToolsDataType']
|
||||||
|
sortedSPDeveloperToolsDataType = spdeveloperToolsDataType.sort_by { |hash| hash['spdevtools_version'].split(' ').first.to_i } # sort by increasing the version of xcode
|
||||||
|
default_xcode_version = sortedSPDeveloperToolsDataType.last['spdevtools_version'] # take the largest version in format: "13.0 (13A5212g)"
|
||||||
|
default_xcode_version_number = default_xcode_version.split(' ').first # take version number
|
||||||
|
|
||||||
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(lane_name, configuration, xcodeproj_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if xcode_version.nil?
|
||||||
|
xcversion(version: default_xcode_version_number)
|
||||||
|
else
|
||||||
|
xcversion(version: xcode_version)
|
||||||
|
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] || "latest"
|
||||||
configuration_type = Touchlane::ConfigurationType.from_type("development")
|
build_number = options[:build_number]
|
||||||
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|
|
||||||
|
|
@ -242,78 +279,122 @@ private_lane :openKeychain do |options|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_default_options
|
lane :ManuallyUpdateCodeSigning do |options|
|
||||||
{
|
register_local_storage_for_match()
|
||||||
:git_url => get_signing_identities_path(),
|
|
||||||
:signing_identities_path => get_signing_identities_path(),
|
require 'match'
|
||||||
:storage_mode => Touchlane::LocalStorage::STORAGE_TYPE
|
|
||||||
}
|
storage_factory = lambda do
|
||||||
|
new_storage = Match::Storage.for_mode('local', { git_url: get_signing_identities_path() })
|
||||||
|
new_storage.download
|
||||||
|
return new_storage
|
||||||
|
end
|
||||||
|
|
||||||
|
encryption_factory = lambda do |stor|
|
||||||
|
new_encryption = Match::Encryption.for_storage_mode('local', { 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
|
||||||
|
|
||||||
|
def sync_code_signing_using_options(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],
|
||||||
|
team_id: options[:team_id],
|
||||||
|
type: options[:type],
|
||||||
|
readonly: options[:readonly].nil? ? true : options[:readonly],
|
||||||
|
storage_mode: "local",
|
||||||
|
# we can't pass signing_identities_path as parameter name since params is hardcoded in match/runner.rb
|
||||||
|
git_url: get_signing_identities_path(),
|
||||||
|
skip_docs: true,
|
||||||
|
keychain_name: options[:keychain_name],
|
||||||
|
keychain_password: options[:keychain_password]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_local_storage_for_match
|
||||||
|
Match::Storage.register_backend(type: 'local', storage_class: Touchlane::LocalStorage)
|
||||||
|
Match::Encryption.register_backend(type: 'local', encryption_class: Match::Encryption::OpenSSL)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_signing_identities_path
|
def get_signing_identities_path
|
||||||
File.expand_path "../EncryptedSigningIdentities"
|
File.expand_path "../EncryptedSigningIdentities"
|
||||||
end
|
end
|
||||||
|
|
||||||
def fill_up_options_using_configuration_type(options, configuration_type, keychain_password_required = false)
|
def fill_up_options_using_configuration_type(options, configuration_type)
|
||||||
configuration = get_configuration_for_type(configuration_type.type)
|
configuration = get_configuration_for_type(configuration_type.type)
|
||||||
|
|
||||||
api_key_path = File.expand_path "../fastlane/#{configuration_type.prefix}_api_key.json"
|
|
||||||
is_api_key_file_exists = File.exists?(api_key_path)
|
|
||||||
|
|
||||||
# default_options required to be empty due to the possibility of skipping the configuration type check below
|
|
||||||
|
|
||||||
default_options = get_default_options
|
|
||||||
|
|
||||||
# Check whether configuration type is required to configure one of api key parameters or not
|
|
||||||
|
|
||||||
if configuration_type.is_app_store || configuration_type.is_development
|
if configuration_type.is_app_store || configuration_type.is_development
|
||||||
|
api_key_path = "fastlane/#{configuration_type.prefix}_api_key.json"
|
||||||
# Check whether API key JSON file exists or not
|
else
|
||||||
|
api_key_path = nil
|
||||||
if is_api_key_file_exists
|
|
||||||
|
|
||||||
# If exists then fill in all required information through api_key_path parameter
|
|
||||||
# and set a value to an options` parameter respectively
|
|
||||||
|
|
||||||
default_options[:api_key_path] = api_key_path
|
|
||||||
else
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
default_options[:api_key] = get_app_store_connect_api_key()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
default_options = {:api_key_path => api_key_path}
|
||||||
|
|
||||||
default_options
|
default_options
|
||||||
.merge(configuration.to_options)
|
.merge(configuration.to_options)
|
||||||
.merge(get_keychain_options(options, keychain_password_required))
|
.merge(get_keychain_options(options))
|
||||||
.merge(options)
|
.merge(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_app_store_connect_api_key()
|
def get_keychain_options(options)
|
||||||
require 'json'
|
|
||||||
|
|
||||||
api_key_parameters = JSON.parse(ENV['API_KEY_JSON'])
|
|
||||||
|
|
||||||
return app_store_connect_api_key(
|
|
||||||
key_id: api_key_parameters['key_id'],
|
|
||||||
issuer_id: api_key_parameters['issuer_id'],
|
|
||||||
key_content: api_key_parameters['key'],
|
|
||||||
duration: api_key_parameters['duration'],
|
|
||||||
in_house: api_key_parameters['in_house']
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_keychain_options(options, keychain_password_required = false)
|
|
||||||
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
|
||||||
)
|
)
|
||||||
|
|
@ -332,6 +413,34 @@ 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/GoogleService-Info.plist"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def generate_enabled_features_extension_if_needed(options)
|
||||||
|
app_target_folder_name = options[:appName] || $appName
|
||||||
|
|
||||||
|
project_enabled_features_file_path = File.expand_path "../#{app_target_folder_name}/Resources/Features/Enabled.swift"
|
||||||
|
build_settings_file_path = File.expand_path "../common/build_settings.yaml"
|
||||||
|
|
||||||
|
unless is_feature_extension_needed?(options, project_enabled_features_file_path)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if options[:features].nil?
|
||||||
|
builder_features_list = [] # If Enabled.swift exists and features option is nil we need to create empty extension to avoid unexpected features
|
||||||
|
else
|
||||||
|
builder_features_list = options[:features]
|
||||||
|
.split(",").map { |feature_name| feature_name.strip } # [ "Feature1", "Feature2", "Feature3" ]
|
||||||
|
end
|
||||||
|
|
||||||
|
build_settings_features_list = Managers::FileManager.load_from_file_YAML(build_settings_file_path)["features"]
|
||||||
|
|
||||||
|
enabled_features_extension = Touchlane::Features.generate_enabled_features_extension(builder_features_list, build_settings_features_list)
|
||||||
|
|
||||||
|
Managers::FileManager.save_data_to_file(project_enabled_features_file_path, enabled_features_extension)
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_feature_extension_needed?(options, project_enabled_features_file_path)
|
||||||
|
!options[:features].nil? || File.exists?(project_enabled_features_file_path)
|
||||||
|
end
|
||||||
|
|
||||||
def set_xcconfig_for_configuration_of_project(lane_name, configuration, xcodeproj_path)
|
def set_xcconfig_for_configuration_of_project(lane_name, configuration, xcodeproj_path)
|
||||||
require 'xcodeproj'
|
require 'xcodeproj'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit a8f072f216684bd7f5563c0211f71d6637a5f92d
|
Subproject commit 3f81529c1425a74f43fe84fe8befffd81a442cc7
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -5,12 +5,10 @@ targets:
|
||||||
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 +16,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: "**********"
|
||||||
|
|
@ -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,122 @@ 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
|
||||||
|
|
||||||
|
# Generate missing properties if needed
|
||||||
|
def generate_missing_properties(target_name, properties, distribution_type)
|
||||||
|
result = []
|
||||||
|
development_team_key = "DEVELOPMENT_TEAM"
|
||||||
|
bundle_id_key = "PRODUCT_BUNDLE_IDENTIFIER"
|
||||||
|
code_sign_style_key = "CODE_SIGN_STYLE"
|
||||||
|
|
||||||
|
# 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?(code_sign_style_key)
|
||||||
|
result.append(config_option(code_sign_style_key, "Manual"))
|
||||||
|
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|
|
||||||
|
if config["xcconfig_options"].any? { |option| key == option["key"] }
|
||||||
|
config["xcconfig_options"].map! { |option| key == option["key"] ? config_option(key, 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)
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ module Touchlane
|
||||||
class LocalStorage < Match::Storage::Interface
|
class LocalStorage < Match::Storage::Interface
|
||||||
attr_accessor :signing_identities_path
|
attr_accessor :signing_identities_path
|
||||||
|
|
||||||
STORAGE_TYPE = "local"
|
|
||||||
|
|
||||||
def self.configure(params)
|
def self.configure(params)
|
||||||
return self.new(
|
return self.new(
|
||||||
# we can't pass signing_identities_path since params is hardcoded in match/runner.rb
|
# we can't pass signing_identities_path since params is hardcoded in match/runner.rb
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
module Touchlane
|
module Touchlane
|
||||||
require_relative "touchlane/configuration_type"
|
require_relative "touchlane/configuration_type"
|
||||||
require_relative "touchlane/configuration"
|
require_relative "touchlane/configuration"
|
||||||
require_relative "match/storage/local_storage_register"
|
require_relative "touchlane/features"
|
||||||
require_relative "touchlane/actions/sync_signing_identities"
|
require_relative "match/storage/local_storage"
|
||||||
require_relative "touchlane/actions/refresh_profiles"
|
|
||||||
require_relative "touchlane/actions/replace_development_certificate"
|
|
||||||
require_relative "touchlane/actions/manually_update_code_signing"
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,13 @@ 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
|
||||||
|
|
@ -32,10 +30,6 @@ module Touchlane
|
||||||
@export_method = "app-store"
|
@export_method = "app-store"
|
||||||
@configuration = "AppStore"
|
@configuration = "AppStore"
|
||||||
@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
|
||||||
|
|
@ -53,11 +47,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 +57,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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
require_relative '../../../../managers/managers'
|
||||||
|
require_relative '../../../../templates/templates'
|
||||||
|
|
||||||
|
module Touchlane
|
||||||
|
class Features
|
||||||
|
|
||||||
|
def self.generate_enabled_features_extension(builder_features_list, build_settings_features_list)
|
||||||
|
|
||||||
|
# Check is entered features contains in configuration file
|
||||||
|
features_diff = builder_features_list - build_settings_features_list
|
||||||
|
|
||||||
|
unless features_diff.empty?
|
||||||
|
raise "Unexpected features: " + features_diff.join(', ')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate enabled features extension from feature names
|
||||||
|
enabled_features_extension_template = Templates::FeatureTemplates.enabled_features_extension
|
||||||
|
utils = Managers::TemplateManager.new(builder_features_list)
|
||||||
|
|
||||||
|
utils.render(enabled_features_extension_template).strip
|
||||||
|
end
|
||||||
|
|
||||||
|
private_class_method :new
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
require 'yaml'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
module Managers
|
||||||
|
class FileManager
|
||||||
|
|
||||||
|
def self.save_data_to_file(path, data)
|
||||||
|
unless File.exists? path
|
||||||
|
raise "Unable to save data to file at #{path}"
|
||||||
|
else
|
||||||
|
File.open(path, "w") do |f|
|
||||||
|
f.write(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.load_from_file_YAML(path)
|
||||||
|
unless File.exists? path
|
||||||
|
raise "Unable to load data from file at #{path}"
|
||||||
|
else
|
||||||
|
YAML.load_file(path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.save_data_to_file_in_json(path, data)
|
||||||
|
json_data = JSON.pretty_generate(data)
|
||||||
|
save_data_to_file(path, json_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
private_class_method :new
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
require 'erb'
|
||||||
|
|
||||||
|
module Managers
|
||||||
|
class TemplateManager
|
||||||
|
|
||||||
|
include ERB::Util
|
||||||
|
|
||||||
|
attr_accessor :items
|
||||||
|
|
||||||
|
def initialize(items)
|
||||||
|
@items = items
|
||||||
|
end
|
||||||
|
|
||||||
|
def render(template)
|
||||||
|
ERB.new(template).result(binding)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
module Managers
|
||||||
|
require_relative "lib/file_manager"
|
||||||
|
require_relative "lib/template_manager"
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
module Templates
|
||||||
|
require_relative "templates/features_templates"
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
module Templates
|
||||||
|
module FeatureTemplates
|
||||||
|
|
||||||
|
def self.features_enum
|
||||||
|
"
|
||||||
|
// MARK: - Generated feature toggles
|
||||||
|
|
||||||
|
public enum Feature: String, Codable, RawRepresentable, CaseIterable {
|
||||||
|
<% for @item in @items %>
|
||||||
|
case <%= @item %>
|
||||||
|
<% end %>
|
||||||
|
}
|
||||||
|
"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.enabled_features_extension
|
||||||
|
"
|
||||||
|
// MARK: - Generated enabled features
|
||||||
|
|
||||||
|
public extension Feature {
|
||||||
|
|
||||||
|
static var enabled: [Feature] {
|
||||||
|
[
|
||||||
|
<% for @item in @items %>
|
||||||
|
\.<%= @item %>,
|
||||||
|
<% end %>
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue