How and why to build an Android application in a docker container
Good afternoon, dear readers.
I’m Vladimir, my name is devops. They say that devops is a disease and I will prove it to you today.
The answer to the question “why?” you won’t find it here, it’s clickbait, I don’t know myself. Everything that happens is motivated by the motto “bekoz ai ken”.
Joking aside, you can run the build process, create isolated test cases, run through autotests, SonarQube and other SASTs
So. Given. One crazy devops, one unfortunate android developer building apk locally. Unfortunately, the situation is widespread.
Part 1. Dockerfile. How to assemble
How do we decide. We write Dockerfile for business and Jenkinsfile for show-off, today we are only talking about Dockerfile.
For a successful process, you need lubricate pull out from the developer the environment in which all this joy is collected. we are interested in gradle, jdk, android sdk versions
It is most convenient to assemble in a ready-made official way https://hub.docker.com/_/gradle
When choosing an image, pay attention to the version of gradle and java, gradle has debian roots, so apt-get java in case there is no version ready. Create a Dockerfile at the root of the turnip and start filling it out:
Immediately check if the versions of gradle and java converge
FROM gradle:6.1.1-jdk11
RUN java -version
RUN gradle -v
because Dockerfile is at the root of the repository, copy the contents to the container
RUN mkdir /opt/project
COPY . /opt/project
WORKDIR /opt/project
Two of the three are – gradle and jdk are installed, install andriodsdk.
There is one nuance. For andriodsdk, you need to pull out the archiver with it. You can smoke the latest version, or you can use pens watch here.
As a devops – I wouldn’t like to go back to updating the environment by hand, however, there may be unpleasant surprises with new versions of andriodsdk. And there is not always a need to use latest. So just put the name of the archive in a variable
ARG ANDROID_SDK_VERSION=8092744
ENV ANDROID_SDK_ROOT /opt/android-sdk
download. put. we clean the trash.
RUN mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools && \
wget https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip && \
unzip *tools*linux*.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && \
mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/tools && \
rm *tools*linux*.zip
That feeling when you didn’t have time to start, but almost everything is ready.
Specify PATH for androidsdk, where gradle will go
ENV PATH ${PATH}:${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin:${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin:${ANDROID_SDK_ROOT}/platform-tools:${ANDROID_SDK_ROOT}/emulator
Pay attention to the second line, where the androidsdk packages are listed separately
In earlier versions, it was enough to install androidsdk, but now you need to install packages \ platforms \ samovars \ harlots separately. Nobody bothers to put all the stuffing at once, butoo …
But the feeling of beauty says that you need only what you need. How to determine which sobsna packages are needed? about it below about the nuances
RUN yes | sdkmanager --licenses && \
sdkmanager "platforms;android-29" "platforms;android-30" "platforms;android-31" "build-tools;29.0.2" && \
cd /opt/project
Last but not least, let’s do some magic.
RUN gradle build
Entirely great Dockerfile
Dockerfile
FROM gradle:6.1.1-jdk11
RUN mkdir /opt/project
COPY . /opt/project
WORKDIR /opt/project
ARG ANDROID_SDK_VERSION=8092744
ENV ANDROID_SDK_ROOT /opt/android-sdk
RUN mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools && \
wget https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip && \
unzip *tools*linux*.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && \
mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/tools && \
rm *tools*linux*.zip
ENV PATH ${PATH}:${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin:${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin:${ANDROID_SDK_ROOT}/platform-tools:${ANDROID_SDK_ROOT}/emulator
RUN yes | sdkmanager --licenses && \
sdkmanager "platforms;android-29" "platforms;android-30" "platforms;android-31" "build-tools;29.0.2" && \
cd /opt/project
RUN gradle build
Now about the nuances.
1. How to determine the sdkmanager packages required for installation?
You can see in the studio \ ask the developer. most likely there will be a lot of unnecessary, so the variant is rooted in a ruthless ru-development.
sequentially run gradle build without sdkmanager-a packages, look at what it swears at – set (i.e. add packages to line 17), repeat until complete self-satisfaction.
2. How to pull out the final apk?
You can add a user and a volume to the Dockerfile, the most obvious.
But in my opinion there is an elegant solution in the form of a Jenkinsfile, because it is a little more than completely composed of sh, you can do the same, but with your hands:
The meaning is that
– create an image (!) from a Dockerfile
docker build .
– the container finished working and closed
status = exited
– we start the container with a command that can continue its work.
docker start build_apk sleep infinity
– we pull out all this joy with a docker and remove the tails
docker cp build_apk:/path/to/apk/in/docker/container/*.apk /path/to/home/dir && /
docker stop build_apk && docker rm build_apk
docker build . -t build_apk
docker run -d build_apk sleep infinity
docker cp build_apk:/path/to/apk/in/docker/container/*.apk /path/to/home/dir
docker stop build_apk && docker rm build_apk
Someone will say “fu, crutch” and will be right. But this decision does not claim to be the ultimate truth. This is a simple and easy-to-learn option that solves a tactical problem.
3. What if we are behind a proxy?
for wget add key “–no-check-certificate”
for sdk manager add keys “–proxy=http –proxy_host=–proxy_port= “
RUN mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools && \
wget https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip --no-check-certificate && \
unzip *tools*linux*.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && \
mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/tools && \
rm *tools*linux*.zip
RUN yes | sdkmanager --licenses --proxy=http --proxy_host=<ip> --proxy_port=<port>&& \
sdkmanager --proxy=http --proxy_host=<ip> --proxy_port=<port> && \
cd /opt/project
Don’t forget to set the proxy in gradle.properies
systemProp.http.proxyHost=<ip>
systemProp.http.proxyPort=<port>
systemProp.https.proxyHost=<ip>
systemProp.https.proxyPort=<port>
#сходу бонус
#чтобы сборка не обжирались и не падала с ошибкой "плак плак, хатю память"
#выставим ограничение
org.gradle.jvmargs=-Xmx2048m -Dkotlin.daemon.jvm.options\="-Xmx2048M" -XX:MaxPermSize=4096m
If we are pulling from a corporate repo, then do not forget to specify it in build.gradle in two places !!
repositories {
mavenCentral { url "https://corp.repo.example.ru/repository/maven-central/" }
jcenter { url "https://corp.repo.example.ru/repository/jcenter/" }
google { url "https://corp.repo.example.ru/repository/dl.google.com-android/" }
}
allprojects {
repositories {
mavenCentral { url "https://corp.repo.example.ru/repository/maven-central/" }
jcenter { url "https://corp.repo.example.ru/repository/jcenter/" }
google { url "https://corp.repo.example.ru/repository/dl.google.com-android/" }
}
}
PS Intrigue
We are here about devops, and therefore we want smoothies and women automation. and therefore we want the assembly to start after the commit to the turnip and the final apk to fall into some kind of general access. How to do it beautifully – in the next article) about the simplest nginx and Jenkins There will be about dumping version into version. In short, intrigue.