developing android plugin
Сергей Боиштян
developing android plugin android - - PowerPoint PPT Presentation
developing android plugin android team lead TFS Android
Сергей Боиштян
2
android разработчик архитектор team lead один из ведущих #AndroidDevPodcast преподаю в TFS Android один из организаторов «Mosdroid» член программного комитета «Mobius»
3
о проблеме для которой мне понадобился plugin
plugin о том как связать task из своего plugin со сторонними
4
я стал «Лидом»/ «Архитектором» появилось много ответственностей связанных с процессом например сделать apk для тестирования
5
саму сборку сделать просто:
параллельно
не просто понять, что тестировать на сборке
6
сделать apk versionname, versioncode, archivename связать с версией
1
отправить в hockeyapp, beta
2
перевести задачи в нужный статус
3
проставить внутри задач версию для связи со сборкой
4
7
hockeyAPP JIRA apk cвязать apk c версией статус задач версия в задачах
8
много шагов легко забыть что-нибудь нет явного триггера для действия можно заболеть
9
10
начал искать решения оказалось, что их много и было то, которое я взял за основу
11
есть специальная ветка в git
в нее попадают изменения триггерится сборка на ci она собирает apk аpk попадает в hockeyApp задачи меняют статус versionname берется из локального файла versioncode — teamcity buildnumber versionName, versionCode проставляются в задаче
12
поменять статус задачам
JIRA teamcity git hockeyAPP
проставить в задаче versioncode, versionname APK versionCode=buildNumber versionName=из файла инкремент versionCode commit
13
триггерим вручную для инкремента версии нужно сделать commit versionCode = синтетический buildNumber
14
изменил инкремент версии что позволило автоматизировать триггер, для формирования сборки
15
формат — Semantic Versioning Specification MAJOR.MINOR.PATCH 1.0.0-rc1 1.1.1-rc2 1.2.3-beta
16
git tag -a 1.0.0-rc1 -m ‘message’
17
когда появились задачи в нужном статусе в JIRA
18
доработал https://github.com/gladed/gradle-android-git-version реализовал инкремент Из 1.1.0-rc10 в 1.1.1-rc1 Из 1.1.1-rc1 в 1.1.1-rc2 Из 1.1.10-rc2 в 1.2.1-rc1
19
добавил:
поменять статус задачам
JIRA teamcity hockeyAPP
проставить в задаче versioncode, versionname apk versionCode=git tag versionName=git tag
20
появление задач в Jira инкремент versionCode
21
➔ bash ➔ fastline (ruby) ➔ gradle ➔ etc.
22
нативная система сборки плагинная система возможность писать на java, kotlin, scala :? но кого я обманываю groovy
23
plugin writer plugin writer build script writer plugin writer build script writer app developer
24
легко шарить легче поддерживать легче тестировать
25
➔ это gradle проект ➔ build.gradle ➔ plugin class ➔ descriptor ➔ one or more tasks ➔ extension
26
plugin extention task descriptor build.gradle
27
apply plugin: 'groovy' apply plugin: 'idea' repositories { mavenCentral() } dependencies { compile localGroovy() compile gradleApi() compile 'commons-io:commons-io:2.4' testCompile 'junit:junit:4.12' }
28
cоздание task cоздание extension cвязь с build chain
29
class HockeyAppPlugin implements Plugin<Project> { void apply(Project project) { project.extensions.create('hockeyapp', HockeyAppPluginExtension, project) if (project.plugins.hasPlugin(AppPlugin)) { AppExtension android = project.android android.applicationVariants.all { variant -> def taskName = "upload${variant.name.capitalize()}ToHockeyApp" def uploadTask = project.tasks.create(taskName, HockeyAppUploadTask) uploadTask.group = 'HockeyApp' uploadTask.dependsOn variant.assemble } } } }
30
class HockeyAppPlugin implements Plugin<Project> { void apply(Project project) { project.extensions.create('hockeyapp', HockeyAppPluginExtension, project) if (project.plugins.hasPlugin(AppPlugin)) { AppExtension android = project.android android.applicationVariants.all { variant -> def taskName = "upload${variant.name.capitalize()}ToHockeyApp" def uploadT adTas ask = projec ect.tas asks.cr creat ate(tas askNam ame, Hockey eyAp AppUp UploadT adTas ask) uploadTask.group = 'HockeyApp' uploadTask.dependsOn variant.assemble } } } }
31
class HockeyAppPlugin implements Plugin<Project> { void apply(Project project) { projec ect.ex exten ension ions.cre creat ate('hock
eyapp app', Hockey ckeyAppPlug uginE nExtens xtension
//... } } }
32
class HockeyAppPlugin implements Plugin<Project> { void apply(Project project) { project.extensions.create('hockeyapp', HockeyAppPluginExtension, project) if (project.plugins.hasPlugin(AppPlugin)) { AppExtension android = project.android android.applicationVariants.all { variant -> def taskName = "upload${variant.name.capitalize()}ToHockeyApp" def uploadTask = project.tasks.create(taskName, HockeyAppUploadTask) uploadTask.group = 'HockeyApp' upload
adTask Task.de depe pends ndsOn On variant iant.asse semb mble le
} } } }
33
class HockeyAppUploadTask extends DefaultTask { @TaskAction def upload() throws IOException { //... }
34
class HockeyAppPlugin implements Plugin<Project> { void apply(Project project) { project.extensions.create('hockeyapp', HockeyAppPluginExtension, project) if (project.plugins.hasPlugin(AppPlugin)) { AppExtension android = project.android android.applicationVariants.all { variant -> def taskName = "upload${variant.name.capitalize()}ToHockeyApp" def uploadTask = project.tasks.create(taskName, HockeyAppUploadTask) upload
adTask Task.gro group up = 'Hocke ckeyApp' p'
} } } }
35
hockeyapp { notify = 1 status = 2 notesType = 0 releaseType = 0 teamCityLog = true repositoryUrl = '' variantToApplicationId = [] }
36
class HockeyAppPluginExtension { def Object outputDirectory def File symbolsDirectory = null def String apiToken = null def Map<String, String> variantToApiToken = null def Map<String, String> variantToNotes = null def String status = 2 def String strategy = "add" def String notify = 0 def Map<String, String> variantToNotify = null //... }
37
HockeyAppPluginExtension hockeyApp = project.hockeyapp
38
implementation-class=de.felixschulze.gradle.HockeyAppPlugin apply plugin: ‘descriptor file name’
39
In build.gradle apply plugin: ‘descriptor file name' hockeyapp { //... }
40
есть два плагина
class Hello implements Plugin<Project>{ @Override void apply(Project project) { project.tasks.create('hello') { doLast { println "Hello, it's me" } } } } class IWasWondering implements Plugin<Project> { @Override void apply(Project project) { project.tasks.create('iWasWondering') { doLast { println "I was wondering if after all these years\n" + " You 'd like to meet" } } } }
41
подключаем к проекту
in build.gradle apply plugin: IWasWondering apply plugin: Hello
42 class Hello implements Plugin<Project>{ @Override void apply(Project project) { project.tasks.create('hello') { doLast { println "Hello, it's me" } } } } class IWasWondering implements Plugin<Project> { @Override void apply(Project project) { project.tasks.create('iWasWondering') { dependsOn(project.hello) doLast { println "I was wondering if after all these years\n" + " You 'd like to meet" } } } }
43
./gradlew iwaswondering заработает?
A problem occurred evaluating project ':ui'. > Failed to apply plugin [class 'IWasWondering'] > Could not get unknown property 'hello' for task ':ui:iWasWondering' of type org.gradle.api.DefaultTask.
44
может дело в порядке apply
было in build.gradle
apply plugin: IWasWondering apply plugin: Hello
стало in build.gradle
apply plugin: Hello apply plugin: IWasWondering
45
Executing tasks: [:ui:iWasWondering] :ui:hello Hello it's me :ui:iWasWondering I was wondering if after all these years You'd like to meet BUILD SUCCESSFUL in 0s
46
initialization configuration execution
47
еxecute build.gradle beforeEvaluate afterEvaluate
48
apply plugin: Hello class Hello implements Plugin<Project>{ @Override void apply(Project project) { project.tasks.create('hello') { doLast { println "Hello, it's me" } } } }
cлучайная последовательность apply
49
class IWasWondering implements Plugin<Project> { @Override void apply(Project project) { project.tasks.create('iWasWondering') { doLast { println "I was wondering if after all these years\n" + " You 'd like to meet" } } pro roject ject.wit with { afterEv erEvalua luate { iWa WasWonderin ering.dep epen endsOn On(hel hello) } } } }
50
class Hello implements Plugin<Project> { @Override void apply(Project project) { project.tasks.create('hello') { fina
naliz lizedB dBy('iWa iWasWond Wonderi ering ng')
doLast {
println "Hello, it's me" } } } }
51
Executing tasks: [:ui:hello] :ui:hello Hello it's me :ui:iWasWondering I was wondering if after all these years You'd like to meet BUILD SUCCESSFUL in 0s
52
When you use the «must run after» ordering rule you specify that taskB must always run after taskA, whenever both taskA and taskB will be run. This is expressed as taskB.mustRunAfter(taskA)
53
class IWasWondering implements Plugin<Project> { @Override void apply(Project project) { project.tasks.create('iWasWondering') { doLast { println "I was wondering if after all these years\n" + " You 'd like to meet" } mustRunA
unAft fter(proj project ect.he hello llo)
} } }
54
Hello it’s me I was wondering if after all these years You’d like to meet
I was wondering if after all these years You’d like to meet Hello it’s me
спрогнозировать
55
Executing tasks: [hell llo, iWasWonde
ing] :ui:hello Hello, it's me :ui:iWasWondering I was wondering if after all these years You 'd like to meet BUILD SUCCESSFUL in 0s
56 class Hello implements Plugin<Project>{ @Override void apply(Project project) { project.tasks.create('hello') { doLast { println "Hello, it's me" } } } } class IWasWondering implements Plugin<Project> { @Override void apply(Project project) { project.tasks.create('iWasWondering') { dependsOn(project.hello) doLast { println "I was wondering if after all these years\n" + " You 'd like to meet" } } } }
57
Hello it’s me Hello it’s me I was wondering if after all these years You’d like to meet
Hello it’s me I was wondering if after all these years You’d like to meet Hello it’s me
Hello it’s me I was wondering if after all these years You’d like to meet
спрогнозировать
58
в какой фазе выполняется?
android { defaultConfig { versionCode androidVersionCode() versionName androidVersionName() } } def androidVersionName() {} def androidVersionCode() {}
59
60
UP-TO-DATE skip tasks debug scripts тестирование
61
кучу готовых решений на рынке нужно разобраться в тонкостях автоматизируйте рутину
62
semantic versioning specification github.com/gladed/gradle-android-git-version github.com/x2on/gradle-hockeyapp-plugin