Compare commits

...

56 Commits

Author SHA1 Message Date
rzaitov 5da1a6efa0 Update and rename readme.md to readme.txt 2014-06-02 20:11:19 +04:00
rzaitov e532e485cb Rename readme.txt to readme.md 2014-06-02 20:10:40 +04:00
rzaitov c8c2a84ceb Update readme.txt 2014-06-02 20:09:58 +04:00
Rustam Zaitov 9277793568 В обобщенные шаги добавил проставление bandleid 2014-06-02 19:40:01 +04:00
Rustam Zaitov b3f5305208 Реализовал поддержку сборки с разными профилями обеспечения 2014-06-02 15:09:22 +04:00
Rustam Zaitov f7e1e7797e для сборки ios проектов всегда собирается ipa файл 2014-02-21 12:56:47 +04:00
rzaitov 5ab8823237 Update readme.txt 2014-02-21 11:27:25 +03:00
Rustam Zaitov 44a32011b8 исправлена ошибка в названии настройки короткой версии
CFShortBundleVersion заменено на CFBundleShortVersionString
2014-01-17 13:36:07 +04:00
Rustam Zaitov 83abffdf33 убрал имя файла из комманды бэкапа 2014-01-17 13:23:14 +04:00
Rustam Zaitov cad3af5a5f вернул команду восстановления из бэкапа 2014-01-17 13:12:53 +04:00
Rustam Zaitov c412b15416 Merge branch 'master' of github.com:rzaitov/BuildScript 2014-01-17 12:15:51 +04:00
Rustam Zaitov 93a6c5d111 По просьбе Антона в выходнои имени файла номер билда отделяется от номера версии дефисом а не точкой 2014-01-17 12:14:54 +04:00
rzaitov b38b1e351a Update readme.txt 2014-01-16 20:54:46 +03:00
Rustam Zaitov 707c091a8d Убрал дублирование из скриптов 2014-01-16 21:38:43 +04:00
Rustam Zaitov aa5da41bdd Обновил сэмпл настроек 2014-01-16 21:23:20 +04:00
rzaitov e2ee11fe6f Update readme.txt 2014-01-16 20:12:28 +03:00
Maxim Smirnov 78b05e7109 навел порядок с именованием артифактов 2014-01-16 20:45:33 +04:00
Maxim Smirnov 0db2b0aa39 исправил ошибку в задании app файла 2014-01-16 20:17:41 +04:00
Maxim Smirnov 896ffc7af6 Задаем название app файла 2014-01-16 20:03:20 +04:00
Rustam Zaitov 4309cedffb убрал ошибку хардкодинга константы 2014-01-16 19:40:29 +04:00
Rustam Zaitov 3972439ba6 Merge branch 'common-steps' 2013-12-30 17:48:47 +04:00
Rustam Zaitov 68415f6313 исправил путаницу с названием проекта и именем файла проекта 2013-12-30 17:33:34 +04:00
Rustam Zaitov 49158c43ca Добавил пояснение к sln конфигурации андройда 2013-12-30 17:16:02 +04:00
Rustam Zaitov 98af62bec5 Merge branch 'common-steps' 2013-12-30 16:47:41 +04:00
Rustam Zaitov f9912d3fbf добавил инструкцию 2013-12-30 16:47:22 +04:00
Rustam Zaitov 3ddc372634 заменил название переменной project_path на project_name поскольку требуется именно имя файла а не путь к нему 2013-12-30 16:08:34 +04:00
Rustam Zaitov a052b1cfe3 Добавил пример настроек, которые необходимо переопределять в проекте 2013-12-30 14:22:01 +04:00
Rustam Zaitov 5e9c4614e1 Merge branch 'common-steps' 2013-12-30 12:42:42 +04:00
Rustam Zaitov 54ae0b6141 Добавил универсальные шаги сборки реализованные Антоном 2013-12-30 12:41:29 +04:00
rzaitov b772326d82 Merge branch 'BS-52' 2013-11-18 15:51:01 +04:00
rzaitov 24b8dbf834 Добился правильной сборки android проекта 2013-11-18 15:50:20 +04:00
rzaitov d364c7cc94 задал шаги для ios сборок 2013-11-15 21:08:54 +04:00
rzaitov 25b58a1548 Нашел ошибку. По каким-то причинам всегда собирается в bin/Debug ??? 2013-11-15 21:00:40 +04:00
rzaitov b87b0b5b37 Исправил ошибку в андроид шагах 2013-11-15 20:40:39 +04:00
rzaitov 3d7a40dd23 Подготовил к сборке 3 конфигураций 2013-11-15 20:38:07 +04:00
rzaitov 7043276c9c Пофиксил ключ по кторому берутся файлы и папки для бэкапа 2013-11-15 20:27:51 +04:00
rzaitov 73cd858826 Добавил возможность определять backup ignore файлы/папки 2013-11-15 20:24:13 +04:00
rzaitov 3f27f5d9ce Merge branch 'BS-51' 2013-11-15 19:15:07 +04:00
rzaitov 4feaa0992d Реализовал команду подписи apk файла 2013-11-15 19:14:34 +04:00
rzaitov 4939004dca Добавил команду подписи apk файла 2013-11-15 19:04:19 +04:00
rzaitov 53edc99ad9 Добавил новую конфигурацию Release для андроида 2013-11-15 18:28:46 +04:00
rzaitov 31dc59ad10 Временно не компилирую проекты с ошибками 2013-11-15 14:56:49 +04:00
rzaitov 9fc5967c1d Merge branch 'BS-47' 2013-11-14 20:52:45 +04:00
rzaitov 95c1f34fc6 подключил патчинг манифеста к движкку 2013-11-14 20:52:17 +04:00
rzaitov 3e66f2a8c2 Merge branch 'BS-47' 2013-11-14 20:29:26 +04:00
rzaitov 65cc154185 поправил предупреждения code inspect 2013-11-14 20:27:25 +04:00
rzaitov 0745de4f20 еще один тест на формирование имени 2013-11-14 20:21:50 +04:00
rzaitov a333991516 Добавил тесты на Manifest патчер 2013-11-14 20:16:36 +04:00
rzaitov b183e05da0 Добавил тест для проверки формирования имен с namespace префиксом 2013-11-14 20:08:44 +04:00
rzaitov cf255b7b1b реализовал патчинг manifest файла 2013-11-14 19:59:12 +04:00
rzaitov f46c5e0c78 Начал реализовывать комманду патчинга андроид манифеста 2013-11-14 17:24:26 +04:00
rzaitov 0c7be54d43 Merge branch 'BS-46' 2013-11-14 16:31:46 +04:00
rzaitov a2b4c18d7f Добавлен манифест для android приложения 2013-11-14 16:31:17 +04:00
rzaitov 6314447f26 Протестировал возможность загрузги дефолтных шагов 2013-11-14 16:30:16 +04:00
rzaitov ed9836fc27 Предоставил возможность пользоваться файлом с дефолтными шагами 2013-11-14 16:26:18 +04:00
rzaitov c2f6338103 Merge branch 'BS-44' 2013-11-14 16:03:39 +04:00
49 changed files with 569 additions and 56 deletions

View File

@ -15,14 +15,16 @@ Global
Release|iPhoneSimulator = Release|iPhoneSimulator
Debug|iPhone = Debug|iPhone
Release|iPhone = Release|iPhone
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Debug|iPhone.Build.0 = Debug|Any CPU
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Release|Any CPU.Build.0 = Release|Any CPU
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Release|iPhone.ActiveCfg = Release|Any CPU
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Release|iPhone.Build.0 = Release|Any CPU
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{10AA179F-818F-4E3F-947E-DE1C0C87BB76}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{3DE4FDFA-1502-44CF-9B73-78B6D730C59F}.Debug|iPhone.ActiveCfg = Debug|iPhone
@ -30,13 +32,13 @@ Global
{3DE4FDFA-1502-44CF-9B73-78B6D730C59F}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{3DE4FDFA-1502-44CF-9B73-78B6D730C59F}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{3DE4FDFA-1502-44CF-9B73-78B6D730C59F}.Release|iPhone.ActiveCfg = Release|iPhone
{3DE4FDFA-1502-44CF-9B73-78B6D730C59F}.Release|iPhone.Build.0 = Release|iPhone
{3DE4FDFA-1502-44CF-9B73-78B6D730C59F}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{3DE4FDFA-1502-44CF-9B73-78B6D730C59F}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{BD5EC0A1-EDC9-4D90-BACF-AE54F26148C1}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{BD5EC0A1-EDC9-4D90-BACF-AE54F26148C1}.Debug|iPhone.Build.0 = Debug|Any CPU
{BD5EC0A1-EDC9-4D90-BACF-AE54F26148C1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{BD5EC0A1-EDC9-4D90-BACF-AE54F26148C1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{BD5EC0A1-EDC9-4D90-BACF-AE54F26148C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD5EC0A1-EDC9-4D90-BACF-AE54F26148C1}.Release|iPhone.ActiveCfg = Release|Any CPU
{BD5EC0A1-EDC9-4D90-BACF-AE54F26148C1}.Release|iPhone.Build.0 = Release|Any CPU
{BD5EC0A1-EDC9-4D90-BACF-AE54F26148C1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU

View File

@ -17,6 +17,7 @@
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<AssemblyName>DroidApp</AssemblyName>
<TargetFrameworkVersion>v4.1</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -52,6 +53,7 @@
<ItemGroup>
<None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" />
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\Main.axml" />

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="DroidApp.DroidApp">
<uses-sdk />
<application android:label="DroidApp">
</application>
</manifest>

47
readme.txt Normal file
View File

@ -0,0 +1,47 @@
Добавить символьную ссылку на скрипт сборки. Заменить <builder_path> на абсолютный путь к корню репозитория билд скрипта
sudo ln -s <builder_path>/scripts/TouchinBuild/taskRunner.py /usr/local/bin/tibuild
Чтобы работала система сборки необходимо выполнить формальные шаги:
0. [iOS] Убедиться что в собираемом проекте выбор профиля обеспечения и сертификата производится автоматически.
Так следует сделать чтобы любой разработчик мог собрать проект, на билд сервере будут подставлены необоходимы значения автоматически
1. [iOS, Android] Убедиться что в названии проекта нет пробелов.
2. [iOS, Android] В корне репозитория создать папку scripts
mkdir scripts
3. [iOS] Положить в папку scripts профили обеспечения со следующими названиями
development.mobileprovision
distribution.mobileprovision
4. [iOS, Android] В папке scripts создать файл settings.txt
touch scripts/settings.txt
5. [iOS, Android] Скопировать содержимое примера scripts/common/setting.txt в свой файл settings.txt и переопределить все необходимые настройки
Стоит обратить внимание на комментации
# required эти настройки необходимо задать, иначе ничего не будет работать
# dont change это можно менять если есть четкое осознание того что происходит
6. [iOS, Android] вызвать скрипт, заменив параметры
на сервере. <builder_path> скорее всего это /BuildServer/Scripts
tibuild --settings=scripts/settings.txt build=%build.number% builder_path=<builder_path>
локально. path_to_local_direcotry путь к папке вне репозитория проекта (чтобы ничего не потерлось) или добавить папку в настройку backup_ignore
tibuild --settings=scripts/settings.txt build=777 builder_path=<builder_path> publish_path=<path_to_local_direcotry>
Пояснение значения некоторых настроек:
publish_step_type enum(development|distribution) в зависимости от этого значения будет вызван один из следующих шагов
'ios publish development.txt' копирование файла ipa в папку @publish_path/
'ios publish distribution.txt' - создание zip архива (app файла) и копирование его в папку @publish_path/
Это значение было введено чтобы поддерживать сборку с разными профилями обеспечения.
Типичный кейс. У нас 2 профайла:
development.mobileprovision сборка для наших тестировщиков [publish_step_type=development]
distribution.mobileprovision сборка для апстора [publish_step_type=distribution]
Расширенный кейс. У нас 3 профайла
development.mobileprovision сборка для наших тестировщиков [publish_step_type=development]
customer.mobileprovision сборка для тестировщиков заказчиков [publish_step_type=development]
distribution.mobileprovision сборка для апстора [publish_step_type=distribution]
bundle_id BundleId который будет подставлен в Info.Plist файл. Настройка нужна для поддержки сборки с разными профилями обсеспечеиня. Так например в профиле обеспечения предоставляемом заказчиком будет указан другой BunldeId, те не com.touchin.projectname

19
scripts/AndroidSteps.txt Normal file
View File

@ -0,0 +1,19 @@
restore from backup
create backup
inside 'BuildSample/BuildSample.sln' remove 'CoolApp:NotCompileApp:Domain' project
inside 'BuildSample/DroidApp/DroidApp.csproj' set OutputPath to 'Output' for '{@sln_config}'
inside 'BuildSample/DroidApp/Properties/AndroidManifest.xml' set android:versionCode to '17'
inside 'BuildSample/DroidApp/Properties/AndroidManifest.xml' set android:versionName to '1.2.3'
clean 'BuildSample/BuildSample.sln' for '{@sln_config}'
sign android 'BuildSample/BuildSample.sln' for '{@sln_config_build}' project 'DroidApp'
create dirs 'Output/GooglePlay/Artifacts'
sh cp BuildSample/DroidApp/Output/*.apk Output/GooglePlay/Artifacts
sh cp -a BuildSample/DroidApp/Output/ Output/GooglePlay/
restore from backup
delete backup

View File

@ -1,7 +1,7 @@
# restore from backup # восстанавливаем из бэкапа (исходники от сборки предыдущей конфигурации могут быть модифицированными)
# create backup
restore from backup # восстанавливаем из бэкапа (исходники от сборки предыдущей конфигурации могут быть модифицированными)
create backup
sh echo '{@sln_config}'
sh echo 'Hello from setup.txt'
inside 'BuildSample/BuildSample.sln' remove 'NotCompileApp:DroidApp' project

View File

@ -7,7 +7,7 @@ create dirs 'Output/Appstore/Artifacts'
copy 'BuildSample/BuildSample/Output/{@assembly_name}-{@version}.ipa' to 'Output/Appstore/Artifacts'
sh cp -a BuildSample/BuildSample/Output/ Output/Appstore/
publish 'Output/Appstore/Artifacts/{@assembly_name}-{@version}.ipa' to testflight notes = 'Hello' api_token = '{@tf_api_token}' team_token = '{@tf_team_token}'
#publish 'Output/Appstore/Artifacts/{@assembly_name}-{@version}.ipa' to testflight notes = 'Hello' api_token = '{@tf_api_token}' team_token = '{@tf_team_token}'
#restore from backup
#delete backup
restore from backup
delete backup

View File

@ -0,0 +1,12 @@
from parsers.ValuesStriper import ValuesStripper
class BaseBackupCommandBuilder:
def __init__(self, ignoreBackupStr):
if ignoreBackupStr:
splitter = ValuesStripper(',')
values = splitter.strip(ignoreBackupStr)
self.ignoreBackup = values
else:
self.ignoreBackup = []

View File

@ -1,10 +1,11 @@
from CommandBuilders.BuilderBackupCommands.BaseBackupCommandBuilder import BaseBackupCommandBuilder
from commands.BaseBackupCommand.CreateBackupCommand import CreateBackupCommand
from parsers.ParserBackup.CreateBackupParser import CreateBackupParser
class CreateBackupCommandBuilder:
def __init__(self):
pass
class CreateBackupCommandBuilder(BaseBackupCommandBuilder):
def __init__(self, ignoreBackupStr):
BaseBackupCommandBuilder.__init__(self, ignoreBackupStr)
def isCreateBackup(self, line):
assert line is not None
@ -18,5 +19,5 @@ class CreateBackupCommandBuilder:
parser = CreateBackupParser()
parser.parseLine(line)
command = CreateBackupCommand()
command = CreateBackupCommand(self.ignoreBackup)
return command

View File

@ -1,10 +1,11 @@
from CommandBuilders.BuilderBackupCommands.BaseBackupCommandBuilder import BaseBackupCommandBuilder
from commands.BaseBackupCommand.DeleteBackupCommand import DeleteBackupCommand
from parsers.ParserBackup.DeleteBackupParser import DeleteBackupParser
class DeleteBackupCommandBuilder:
def __init__(self):
pass
class DeleteBackupCommandBuilder(BaseBackupCommandBuilder):
def __init__(self, ignoreBackupStr):
BaseBackupCommandBuilder.__init__(self, ignoreBackupStr)
def isDeleteBackup(self, line):
assert line is not None
@ -20,5 +21,5 @@ class DeleteBackupCommandBuilder:
parser = DeleteBackupParser()
parser.parseLine(line)
command = DeleteBackupCommand()
command = DeleteBackupCommand(self.ignoreBackup)
return command

View File

@ -1,10 +1,11 @@
from CommandBuilders.BuilderBackupCommands.BaseBackupCommandBuilder import BaseBackupCommandBuilder
from commands.BaseBackupCommand.RestoreBackupCommand import RestoreBackupCommand
from parsers.ParserBackup.RestoreBackupParser import RestoreBackupParser
class RestoreBackupCommandBuilder:
def __init__(self):
pass
class RestoreBackupCommandBuilder(BaseBackupCommandBuilder):
def __init__(self, ignoreBackupStr):
BaseBackupCommandBuilder.__init__(self, ignoreBackupStr)
def isRestoreBackup(self, line):
assert line is not None
@ -20,5 +21,5 @@ class RestoreBackupCommandBuilder:
parser = RestoreBackupParser()
parser.parseLine(line)
command = RestoreBackupCommand()
command = RestoreBackupCommand(self.ignoreBackup)
return command

View File

@ -0,0 +1 @@
__author__ = 'rzaitov'

View File

@ -0,0 +1,24 @@
from commands.PatchManifestCommand import PatchManifestCommand
from parsers.InsideParser.InsideSetParser import InsideSetParser
class PatchManifestCommandBuilder:
def __init__(self):
pass
def isManifestCommand(self, line):
assert line is not None
parser = InsideSetParser('xml')
isValid = parser.isValidLine(line)
return isValid
def getCommandFor(self, line):
assert line is not None
parser = InsideSetParser('xml')
result = parser.parseLine(line)
command = PatchManifestCommand(result[0], result[1], result[2])
return command

View File

@ -0,0 +1,30 @@
from commands.CleanBuildCommands.SignApkCommand import SignApkCommand
from parsers.SignApkParser import SignApkParser
class SignApkCommandBuilder:
def __init__(self, pathToBuildUtil):
assert pathToBuildUtil is not None
self.pathToBuildUtil = pathToBuildUtil
def isSignApk(self, line):
assert line is not None
parser = SignApkParser()
isValid = parser.isValidLine(line)
return isValid
def getCommandFor(self, line):
assert line is not None
parser = SignApkParser()
result = parser.parseLine(line)
slnPath = result[0]
slnConfig = result[1]
projectName = result[2]
command = SignApkCommand(self.pathToBuildUtil, slnPath, slnConfig, projectName)
return command

View File

@ -1,15 +1,18 @@
from CommandBuilders.BuilderBackupCommands.CreateBackupCommandBuilder import CreateBackupCommandBuilder
from CommandBuilders.BuilderBackupCommands.DeleteBackupCommandBuilder import DeleteBackupCommandBuilder
from CommandBuilders.BuilderBackupCommands.RestoreBackupCommandBuilder import RestoreBackupCommandBuilder
from CommandBuilders.CleanBuildCommandBuilder import CleanBuildCommandBuilder
from CommandBuilders.CopyCommandBuilder import CopyCommandBuilder
from CommandBuilders.CreateBackupCommandBuilder import CreateBackupCommandBuilder
from CommandBuilders.DeleteBackupCommandBuilder import DeleteBackupCommandBuilder
from CommandBuilders.InstallProfileCommandBuilder import InstallProfileCommandBuilder
from CommandBuilders.MakeDirsCommandBuilder import MakeDirsCommandBuilder
from CommandBuilders.PatchCsprojCommandBuilder import PatchCsprojCommandBuilder
from CommandBuilders.PatchInfoPlistArrayCommandBuilder import PatchInfoPlistArrayCommandBuilder
from CommandBuilders.PatchInfoplistCommandBuilder import PatchInfoplistCommandBuilder
from CommandBuilders.PatchManifestCommandBuilder import PatchManifestCommandBuilder
from CommandBuilders.RemoveProjectCommandBuilder import RemoveProjectCommandBuilder
from CommandBuilders.RestoreBackupCommandBuilder import RestoreBackupCommandBuilder
from CommandBuilders.ShCommandBuilder import ShCommandBuilder
from CommandBuilders.SignApkBuilder import SignApkCommandBuilder
from CommandBuilders.TestflightCommandBuilder import TestflightCommandBuilder
@ -24,22 +27,27 @@ class StepsRunner:
self.shCommandBuilder = ShCommandBuilder()
self.removeProjectBuilder = RemoveProjectCommandBuilder()
self.createBackupBuilder = CreateBackupCommandBuilder()
self.restoreFromBackupBuilder = RestoreBackupCommandBuilder()
self.deleteBackupBuilder = DeleteBackupCommandBuilder()
self.createDirs = MakeDirsCommandBuilder()
self.patchCsproj = PatchCsprojCommandBuilder()
self.patchInfoPlist = PatchInfoplistCommandBuilder(self.valueProvider)
self.patchInfoPlistArray = PatchInfoPlistArrayCommandBuilder()
self.patchManifest = PatchManifestCommandBuilder()
self.copyBuilder = CopyCommandBuilder()
self.testflightBuilder = TestflightCommandBuilder()
ignoreBackup = config.get('backup_ignore', None)
self.createBackupBuilder = CreateBackupCommandBuilder(ignoreBackup)
self.restoreFromBackupBuilder = RestoreBackupCommandBuilder(ignoreBackup)
self.deleteBackupBuilder = DeleteBackupCommandBuilder(ignoreBackup)
profilePrefix = config['project_name']
self.installProfileBuilder = InstallProfileCommandBuilder(profilePrefix)
buildUtilPath = config['build_tool']
self.cleanBuilder = CleanBuildCommandBuilder(buildUtilPath, 'clean')
self.buildBuilder = CleanBuildCommandBuilder(buildUtilPath, 'build')
self.signAndroid = SignApkCommandBuilder(buildUtilPath)
def run(self, content):
assert content is not None
@ -62,6 +70,8 @@ class StepsRunner:
cmd = self.cleanBuilder.getCommandFor(line)
elif self.buildBuilder.isCleanBuild(line):
cmd = self.buildBuilder.getCommandFor(line)
elif self.signAndroid.isSignApk(line):
cmd = self.signAndroid.getCommandFor(line)
elif self.createBackupBuilder.isCreateBackup(line):
cmd = self.createBackupBuilder.getCommandFor(line)
elif self.createDirs.isMakeDirsCommand(line):
@ -72,6 +82,8 @@ class StepsRunner:
cmd = self.patchInfoPlist.getCommandFor(line)
elif self.patchInfoPlistArray.isPatchInfoPlist(line):
cmd = self.patchInfoPlistArray.getCommandFor(line)
elif self.patchManifest.isManifestCommand(line):
cmd = self.patchManifest.getCommandFor(line)
elif self.copyBuilder.isCopy(line):
cmd =self.copyBuilder.getCommandFor(line)
elif self.restoreFromBackupBuilder.isRestoreBackup(line):

View File

@ -1,8 +1,8 @@
from CommandBuilders.CreateBackupCommandBuilder import CreateBackupCommandBuilder
from CommandBuilders.BuilderBackupCommands.CreateBackupCommandBuilder import CreateBackupCommandBuilder
line = "create backup"
cmdBuilder = CreateBackupCommandBuilder()
cmdBuilder = CreateBackupCommandBuilder(None)
command = cmdBuilder.getCommandFor(line)
command.execute()

View File

@ -1,8 +1,8 @@
from CommandBuilders.DeleteBackupCommandBuilder import DeleteBackupCommandBuilder
from CommandBuilders.BuilderBackupCommands.DeleteBackupCommandBuilder import DeleteBackupCommandBuilder
line = "delete backup"
cmdBuilder = DeleteBackupCommandBuilder()
cmdBuilder = DeleteBackupCommandBuilder(None)
command = cmdBuilder.getCommandFor(line)
command.execute()

View File

@ -47,6 +47,6 @@ contentProvider = ContentProviderMock()
buildConfigProvider = BuildConfigProvider()
preprocessor = NullPreprocessor()
taskRunner = TaskRunner(settingsProvider, contentProvider, buildConfigProvider, preprocessor)
taskRunner = TaskRunner(settingsProvider, contentProvider, buildConfigProvider, preprocessor, {})
taskRunner.run()

View File

@ -0,0 +1,8 @@
from CommandBuilders.PatchManifestCommandBuilder import PatchManifestCommandBuilder
line = "inside 'BuildSample/DroidApp/Properties/AndroidManifest.xml' set android:versionCode to '7.7.7'"
builder = PatchManifestCommandBuilder()
command = builder.getCommandFor(line)
command.execute()

View File

@ -40,7 +40,7 @@ resolvedBuildConfigProvider = ResolvedBuildConfigProvider(buildConfigProvider)
contentProvider = ContentProviderMock()
preprocessor = NullPreprocessor()
taskRunner = TaskRunner(settingsProvider, contentProvider, resolvedBuildConfigProvider, preprocessor)
taskRunner = TaskRunner(settingsProvider, contentProvider, resolvedBuildConfigProvider, preprocessor, {})
taskRunner.run()

View File

@ -1,8 +1,8 @@
from CommandBuilders.RestoreBackupCommandBuilder import RestoreBackupCommandBuilder
from CommandBuilders.BuilderBackupCommands.RestoreBackupCommandBuilder import RestoreBackupCommandBuilder
line = "restore from backup"
builder = RestoreBackupCommandBuilder()
builder = RestoreBackupCommandBuilder(None)
command = builder.getCommandFor(line)
command.execute()

View File

@ -0,0 +1 @@
__author__ = 'rzaitov'

View File

@ -0,0 +1,25 @@
import unittest
from utils.ManifestPatcher import ManifestPatcher
class TestManifestPatcher(unittest.TestCase):
def setUp(self):
self.patcher = ManifestPatcher('somePath')
def test_parseRawName(self):
nameInfo1 = self.patcher.parseRawName('simpleName')
self.assertEqual(None, nameInfo1['prefix'])
self.assertEqual('simpleName', nameInfo1['original_name'])
nameInfo2 = self.patcher.parseRawName('prefix:originalName')
self.assertEqual('prefix', nameInfo2['prefix'])
self.assertEqual('originalName', nameInfo2['original_name'])
def test_fetchName(self):
nameInfo = {
'prefix': 'android',
'original_name': 'MyName'
}
name = self.patcher.fetchName(nameInfo)
self.assertEqual('{http://schemas.android.com/apk/res/android}MyName', name)

View File

@ -0,0 +1,11 @@
import unittest
from utils.XmlPatcher import XmlPatcher
class TestXmlPatcher(unittest.TestCase):
def setUp(self):
self.patcher = XmlPatcher('somePath')
def test_getNameWithNs(self):
name = self.patcher.getNameWithNs('OriginalName', 'http://namespace')
self.assertEqual('{http://namespace}OriginalName', name)

View File

@ -4,8 +4,14 @@ from commands.CommandBase import CommandBase
class BaseBackupCommand(CommandBase):
def __init__(self):
def __init__(self, ignoreBackup):
CommandBase.__init__(self)
assert ignoreBackup is not None
self.backupIgnore = ['.git', '.gitignore', '.DS_Store', 'backup']
self.backupIgnore.extend(ignoreBackup)
self.folderPath = '.'
# вычислять абсолютные пути надо на этапе создания комманды
@ -13,7 +19,6 @@ class BaseBackupCommand(CommandBase):
self.srcAbsDirPath = self.getAbsSrc()
self.backupDirAbsPath = self.getAbsDst()
self.backupIgnore = ['.git', '.gitignore', '.DS_Store', 'backup']
def getAbsSrc(self):
return self.getAbs(self.folderPath)

View File

@ -4,8 +4,8 @@ from commands.BaseBackupCommand.BaseBackupCommand import BaseBackupCommand
class CreateBackupCommand(BaseBackupCommand):
def __init__(self):
BaseBackupCommand.__init__(self)
def __init__(self, ignoreBackup):
BaseBackupCommand.__init__(self, ignoreBackup)
def execute(self):
#if os.path.exists(self.backupDirAbsPath):

View File

@ -4,8 +4,8 @@ from commands.BaseBackupCommand.BaseBackupCommand import BaseBackupCommand
class DeleteBackupCommand(BaseBackupCommand):
def __init__(self):
BaseBackupCommand.__init__(self)
def __init__(self, ignoreBackup):
BaseBackupCommand.__init__(self, ignoreBackup)
def execute(self):
if not os.path.exists(self.backupDirAbsPath):

View File

@ -4,8 +4,8 @@ from commands.BaseBackupCommand.BaseBackupCommand import BaseBackupCommand
class RestoreBackupCommand(BaseBackupCommand):
def __init__(self):
BaseBackupCommand.__init__(self)
def __init__(self, ignoreBackup):
BaseBackupCommand.__init__(self, ignoreBackup)
def execute(self):
if not os.path.exists(self.backupDirAbsPath):

View File

@ -0,0 +1,23 @@
from commands.ShellCommandBase import ShellCommandBase
class SignApkCommand(ShellCommandBase):
def __init__(self, pathToBuildUtil, slnPath, slnConfig, projectName):
ShellCommandBase.__init__(self)
assert pathToBuildUtil is not None
assert slnPath is not None
assert slnConfig is not None
assert projectName is not None
self.pathToBuildUtil = pathToBuildUtil
self.slnPath = slnPath
self.slnConfig = slnConfig
self.projectName = projectName
self.commandPattern = '{0} -v build "--configuration:{1}" "--project:{2}" /t:SignAndroidPackage "{3}"'
def execute(self):
cmdText = self.commandPattern.format(self.pathToBuildUtil, self.slnConfig, self.projectName, self.slnPath)
print cmdText
self.executeShell(cmdText)

View File

@ -0,0 +1,20 @@
from commands.CommandBase import CommandBase
from utils.ManifestPatcher import ManifestPatcher
class PatchManifestCommand(CommandBase):
def __init__(self, pathToManifest, key, value):
CommandBase.__init__(self)
assert pathToManifest is not None
assert key is not None
assert value is not None
self.pathToManifest = pathToManifest
self.key = key
self.value = value
def execute(self):
patcher = ManifestPatcher(self.pathToManifest)
patcher.AddOrReplaceManifestAtr(self.key, self.value)

View File

@ -19,7 +19,7 @@ class InsideSetParser(InsideParserBase):
def getMatchInfo(self, line):
assert line is not None
keyRegexp = r'(?P<key>[a-zA-Z]+)'
keyRegexp = r'(?P<key>\S+)'
valueRegexp = r"'(?P<value>[^']+)'$"
rb = RegexpBuilder()

View File

@ -0,0 +1,40 @@
import re
from parsers.LineParser import LineParser
from parsers.RegexpBuilder import RegexpBuilder
class SignApkParser(LineParser):
def __init__(self):
LineParser.__init__(self)
def parseLine(self, line):
assert line is not None
filePathRegexp = r"'(?P<path>[./ a-zA-Z]+\.sln)'"
slnConfigRegexp = r"'(?P<config>[a-zA-Z|]+)'"
projectRegexp = r"'(?P<project>[.a-zA-Z]+)'$"
rb = RegexpBuilder()
rSrc = rb.startsWith('sign') + rb.than('android') + filePathRegexp + rb.keywordToken('for') + slnConfigRegexp +\
rb.keywordToken('project') + projectRegexp
regexp = re.compile(rSrc, re.UNICODE)
match = regexp.match(line)
self._guardMatch(match, line, rSrc)
path = match.group('path')
slnConfig = match.group('config')
project = match.group('project')
return path, slnConfig, project
def isValidLine(self, line):
assert line is not None
rb = RegexpBuilder()
rSrc = rb.startsWith('sign') + rb.than('android')
regexp = re.compile(rSrc, re.UNICODE)
match = regexp.match(line)
return match is not None

View File

@ -1,11 +1,13 @@
class ValuesStripper:
def __init__(self):
pass
def __init__(self, separator=':'):
assert separator is not None
self.separator = separator
def strip(self, valueStr):
assert valueStr is not None
rawValues = valueStr.split(':')
rawValues = valueStr.split(self.separator)
values = [name.strip() for name in rawValues]
return values

View File

@ -24,5 +24,6 @@ print 'current working dir: {0}'.format(baseDirAbsPath)
#import Tests.ManualTests.install_profile
#import Tests.ManualTests.macros_include_test
#import Tests.ManualTests.resolve_settings
#import Tests.ManualTests.infoPlistMultipleValues_test
import Tests.ManualTests.infoPlistMultipleValues_test
import Tests.ManualTests.manifest_test

View File

@ -0,0 +1 @@
sh echo 'This is default steps file'

View File

@ -26,17 +26,18 @@ scriptDir = os.path.dirname(scriptFilePath)
class TaskRunner:
def __init__(self, settingsProvider, fileContentProvider, buildConfigProvider, linePreprocessor):
def __init__(self, settingsProvider, fileContentProvider, buildConfigProvider, linePreprocessor, defaults):
assert settingsProvider is not None
assert fileContentProvider is not None
assert buildConfigProvider is not None
assert linePreprocessor is not None
assert defaults is not None
self.settingsProvider = settingsProvider
self.fileContentProvider = fileContentProvider
self.configsProvider = buildConfigProvider
self.linePreprocessor = linePreprocessor
self.defaults = defaults
macroProcessor = MacroProcessor()
self.valueProvider = ValueProvider()
@ -68,7 +69,7 @@ class TaskRunner:
stepsRunner.run(content)
def getStepsContent(self, config):
pathToSteps = config['steps']
pathToSteps = config.get('steps', self.defaults['steps'])
content = self.fileContentProvider.fetchContent(pathToSteps)
content = self.textPreprocessor.processText(content, self.textPreprocessor)
@ -77,6 +78,12 @@ class TaskRunner:
if __name__ == "__main__":
defaults = {
'settings': 'settings.txt',
'builder_path': scriptDir,
'steps': os.path.join(scriptDir, 'steps.txt')
}
parser = argparse.ArgumentParser()
parser.add_argument('--settings', required=False)
allArgs = parser.parse_known_args()
@ -92,7 +99,7 @@ if __name__ == "__main__":
linePreprocessor.addProcessor(lineStripper)
# TODO: перенести в корень комапановки
settingsPath = knownArgs.settings or 'settings.txt'
settingsPath = knownArgs.settings or defaults['settings']
fromFileSettingsProvider = FromFileSettingsProvider(settingsPath, linePreprocessor)
overrideWithCmdSetProvider = CmdArgsOverriderSettingsProvider(fromFileSettingsProvider, overrideArgs, linePreprocessor)
@ -100,10 +107,10 @@ if __name__ == "__main__":
buildConfigProvider = BuildConfigProvider()
predefineBuildConfigProvider = PredefinedMacrosBuildConfigProvider(buildConfigProvider)
predefineBuildConfigProvider.addPredefineMacro('builder_path', scriptDir)
predefineBuildConfigProvider.addPredefineMacro('builder_path', defaults['builder_path'])
resolvedBuildConfigProvider = ResolvedBuildConfigProvider(predefineBuildConfigProvider)
runner = TaskRunner(overrideWithCmdSetProvider, fContentProvider, resolvedBuildConfigProvider, linePreprocessor)
runner = TaskRunner(overrideWithCmdSetProvider, fContentProvider, resolvedBuildConfigProvider, linePreprocessor, defaults)
runner.run()

View File

@ -0,0 +1,55 @@
from utils.XmlPatcher import XmlPatcher
class ManifestPatcher(XmlPatcher):
def __init__(self, manifestPath):
assert manifestPath is not None
XmlPatcher.__init__(self, manifestPath)
self.androidNs = "http://schemas.android.com/apk/res/android"
self.androidNsPrefix = 'android'
self.namespaces[self.androidNsPrefix] = self.androidNs
self.regNamespace(self.androidNsPrefix, self.androidNs)
def AddOrReplaceManifestAtr(self, rawAtrName, atrValue):
assert rawAtrName is not None
assert atrValue is not None
tree = self.parse()
manifestElement = tree.getroot()
name = self.fetchNameByRawName(rawAtrName)
manifestElement.set(name, atrValue)
self.write(tree)
def fetchNameByRawName(self, rawName):
nameInfo = self.parseRawName(rawName)
name = self.fetchName(nameInfo)
return name
def parseRawName(self, rawName):
"""rawName=(nsPrefix:)?OriginalName
"""
result = rawName.split(':')
prefixExists = len(result) > 1
nameInfo = {
'prefix': result[0] if prefixExists else None,
'original_name': result[1] if prefixExists else result[0]
}
return nameInfo
def fetchName(self, nameInfo):
assert nameInfo is not None
nsPrefix = nameInfo['prefix']
origName = nameInfo['original_name']
namespace = self.namespaces.get(nsPrefix, None)
name = self.getNameWithNs(origName, namespace) if nsPrefix else origName
return name

View File

@ -0,0 +1,27 @@
import xml.etree.ElementTree as eT
class XmlPatcher:
def __init__(self, path):
assert path is not None
self.path = path
self.namespaces = {}
def parse(self):
return eT.parse(self.path)
def write(self, tree):
tree.write(self.path, xml_declaration=True, encoding="UTF-8", method="xml")
def regNamespace(self, nsKey, nsValue):
assert nsKey is not None
assert nsValue is not None
eT.register_namespace(nsKey, nsValue)
def getNameWithNs(self, originalName, namespace):
assert originalName is not None
assert namespace is not None
# {someNamespace}OriginalName
return '{{{0}}}{1}'.format(namespace, originalName)

View File

@ -0,0 +1,2 @@
build '{@sln_path}' for '{@sln_config}'
sign android '{@sln_path}' for '{@sln_config}' project '{@csproj_name}'

View File

@ -0,0 +1,9 @@
inside '{@csproj_dir}/{@csproj_file_name}' set OutputPath to '{@output_path}' for '{@sln_config}|AnyCPU'
inside '{@csproj_dir}/{@csproj_file_name}' set AssemblyName to '{@project_name}' for ''
inside '{@csproj_dir}/Properties/AndroidManifest.xml' set package to '{@package_name}'
inside '{@csproj_dir}/Properties/AndroidManifest.xml' set android:versionName to '{@version}.{@build}'
inside '{@csproj_dir}/Properties/AndroidManifest.xml' set android:versionCode to '{@version_code}'
inside '{@sln_path}' remove '{@remove_project}' project
clean '{@sln_path}' for '{@sln_config}'

View File

@ -0,0 +1 @@
copy '{@csproj_dir}/{@output_path}/{@package_name}-Signed.apk' to '{@publish_path}/{@output_file_name}'

View File

@ -0,0 +1,14 @@
restore from backup
create backup
<include '{@builder_path}/scripts/common/android prepare.txt'>
<include '{@builder_path}/scripts/common/android build.txt'>
#if {@teamcity_build_id}
create dirs '{@publish_path}'
sh echo '{@teamcity_build_id}' > '{@publish_path}/../{@build}.build_id'
<include '{@builder_path}/scripts/common/android publish.txt'>
#endif
restore from backup
delete backup

View File

@ -0,0 +1 @@
build '{@sln_path}' for '{@sln_config}'

View File

@ -0,0 +1,15 @@
install profile 'scripts/{@provisioning_profile}.mobileprovision'
inside '{@csproj_dir}/{@csproj_file_name}' set CodesignKey to '{@provisioning_account}' for '{@sln_config}'
inside '{@csproj_dir}/{@csproj_file_name}' set CodesignProvision to '{@provisioning_uudid}' for '{@sln_config}'
inside '{@csproj_dir}/{@csproj_file_name}' set OutputPath to '{@output_path}' for '{@sln_config}'
inside '{@csproj_dir}/{@csproj_file_name}' set IpaPackageName to '{@output_file_format}' for '{@sln_config}'
inside '{@csproj_dir}/{@csproj_file_name}' set BuildIpa to 'true' for '{@sln_config}'
inside '{@csproj_dir}/{@csproj_file_name}' set AssemblyName to '{@project_name}' for ''
inside '{@csproj_dir}/Info.plist' set CFBundleIdentifier to '{@bundle_id}'
inside '{@csproj_dir}/Info.plist' set CFBundleVersion to '{@version}.{@build}'
inside '{@csproj_dir}/Info.plist' set CFBundleShortVersionString to '{@version}'
inside '{@sln_path}' remove '{@remove_project}' project
clean '{@sln_path}' for '{@sln_config}'

View File

@ -0,0 +1 @@
copy '{@csproj_dir}/{@output_path}/{@output_file_format}.ipa' to '{@publish_path}/{@output_file_name}'

View File

@ -0,0 +1,2 @@
sh rm -f '{@publish_path}/{@output_file_name}'
sh cd '{@csproj_dir}/{@output_path}' && zip -y -r '{@publish_path}/{@output_file_name}' '{@csproj_name}.app'

14
scripts/common/ios.txt Normal file
View File

@ -0,0 +1,14 @@
restore from backup
create backup
<include '{@builder_path}/scripts/common/ios prepare.txt'>
<include '{@builder_path}/scripts/common/ios build.txt'>
#if {@teamcity_build_id}
create dirs '{@publish_path}'
sh echo '{@teamcity_build_id}' > '{@publish_path}/../{@build}.build_id'
<include '{@builder_path}/scripts/common/ios publish {@publish_step_type}.txt'>
#endif
restore from backup
delete backup

View File

@ -0,0 +1,67 @@
# global settings
teamcity_build_id = '0'
# Обновляйте это поле руками, когда выходит новая версия приложения
version = '0.0'
# Будет переопределено teamcity. например так build=%build.number%
build = '0'
build_tool = '/Applications/Xamarin\ Studio.app/Contents/MacOS/mdtool'
project_name = '' # required for build server infastructure
# required!!! обратите внимание на регистр папки builds на некоторых проектах название с большой буквы, а на некоторых с маленькой
publish_path = '/BuildServer/{@project_name}/builds/{@publish_name}'
# добавьте названия папок или файлов которые не надо бэкапить перед сборкой конфигурации
# это может быть папка куда складываются артефакты или служебная папка системы контроля версий
backup_ignore = .git, .gitignore, .DS_Store, backup, Artifacts, scripts, settings.txt, ios.txt
# укажите через запятую конфигурации которые необходимо собрать
configs = 'default_ios, appstore, default_android, googleplay'
# required. Возможно для IOs и Android созданы разные sln файлы, тогда эту настройку
# следует перенести в ios и android конфигурации
sln_path = ''
# dont change
csproj_file_name = '{@csproj_name}.csproj'
# эта настройка нужна для единообразия названий билдов
output_file_format = '{@project_name}-{@version}-{@build}'
# required
ios.csproj_dir = ''
ios.csproj_name = '' # только имя проекта, обычно имя файла без расширения
ios.remove_project = '' #оставить пустым если не надо удалять проекты. Чаще всего указывают проект Android (если он в том же солюшене)
# dont change
ios.sln_config = 'Release|iPhone'
ios.steps = '{@builder_path}/scripts/common/ios.txt'
ios.output_path = 'bin'
ios.publish_name = 'iPhone'
ios.default_ios.provisioning_profile = 'development' # required. Имя файла профиля обеспечения без разширерия (development.mobileprovision)
ios.default_ios.publish_step_type = 'development' # required. Enum (appstore|distribution)
ios.default_ios.bundle_id = '' # required. Обычно это com.touchin.projectname
ios.default_ios.provisioning_account = 'iPhone Developer: Build Server (GZRT3GYURD)' #Билд сервер собирает с помощью разработчика Build Server
ios.default_ios.provisioning_uudid = '' # required. Можно использовать пробел для режима Automatic
ios.default_ios.output_file_name = '{@output_file_format}-Default.ipa'
ios.appstore.provisioning_profile = 'distribution' # required. Имя файла профиля обеспечения без разширерия (distribution.mobileprovision)
ios.appstore.publish_step_type = 'distribution' # required. Enum (appstore|distribution)
ios.appstore.bundle_id = '' # required. Обычно это com.touchin.projectname
ios.appstore.provisioning_account = 'iPhone Distribution' # dont change. На билд сервере всего один distribution сертификат Ltd Touchinstinct он и будет выбран
ios.appstore.provisioning_uudid = '' # required. Можно использовать пробел для режима Automatic
ios.appstore.output_file_name = '{@output_file_format}-AppStore.zip'
# required
android.csproj_dir = ''
android.csproj_name = '' # только имя проекта, обычно имя файла без расширения
android.remove_project = '' #оставить пустым если не надо удалять проекты. Чаще всего указывают проект Android (если он в том же солюшене)
android.output_file_name = '{@output_file_format}-Default.apk'
# dont change
android.sln_config = 'Release' # Нельзя указывать платформу(Release|AnyCPU). Иначе приложение не будет подписано
android.steps = '{@builder_path}/scripts/common/android.txt'
android.output_path = 'bin'
android.publish_name = 'Android'
android.version_code = '{@build}' # Возможно стоит переопределить значения в конкректных конфигурациях
android.default_android.package_name = '' #required
android.googleplay.package_name = '' #required

View File

@ -1,12 +1,13 @@
# global settings
build_tool='/Applications/Xamarin\ Studio.app/Contents/MacOS/mdtool'
version=0.0.0 # комментарий в тойже строке
configs = 'appstore, staging'
configs = 'appstore'
project_name = CoolApp
backup_ignore = .git, .gitignore, .DS_Store, backup, Output, scripts
# ios platform settings
ios.steps = scripts/IosSteps.txt
ios.sln_config = Release|iPhone
ios.steps = 'scripts/IosSteps.txt'
ios.setup_steps = 'IosSetupSteps.txt'
ios.assembly_name = 'CoolApp'
@ -14,7 +15,9 @@ ios.tf_api_token = '0e6925075d4fc10fed0e7bbf43fa6894_NjQ0OTI2MjAxMi0wOS0yNSAxMTo
ios.tf_team_token = 'c5c3cf7a6dae2bea4382dfbd181a2075_Mjc4ODkwMjAxMy0wOS0yOSAxNDowOTo1OC40Mzg5MTY'
# android platform settings
# android.steps = 'scripts/AndroidSteps.txt'
android.sln_config = Release|AnyCPU
android.sln_config_build = Release
android.steps = 'scripts/AndroidSteps.txt'
# config settings
ios.appstore.app_name = {@project_name}