initial script and publish action
This commit is contained in:
parent
a1f1eca3f6
commit
e478a4c836
70 changed files with 2602 additions and 0 deletions
26
.github/workflows/publish.yml
vendored
Normal file
26
.github/workflows/publish.yml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
name: Image Publishing
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-images:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_LOGIN_USER }}
|
||||
password: ${{ secrets.DOCKER_LOGIN_TOKEN }}
|
||||
- name: Publish Project
|
||||
run: ./gradlew run --args="$(pwd)/configuration.toml"
|
||||
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
|
||||
outputs
|
||||
4
.idea/.gitignore
generated
vendored
Normal file
4
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
sonarlint
|
||||
117
.idea/codeStyles/Project.xml
generated
Normal file
117
.idea/codeStyles/Project.xml
generated
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<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>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
8
.idea/compiler.xml
generated
Normal file
8
.idea/compiler.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="1.8">
|
||||
<module name="AndroidTestDockerfile.app" target="1.8" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
</project>
|
||||
19
.idea/gradle.xml
generated
Normal file
19
.idea/gradle.xml
generated
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/main" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/kotlinScripting.xml
generated
Normal file
6
.idea/kotlinScripting.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinScriptingSettings">
|
||||
<option name="suppressDefinitionsCheck" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/misc.xml
generated
Normal file
9
.idea/misc.xml
generated
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
153
README.md
Normal file
153
README.md
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# Android Emulator Test Image
|
||||
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
This is a simple Java app to generate and publish my DockerImages.
|
||||
The DockerImages I am planning to use with `/dev/kvm` enabled Containers so I can run my tests androidTests on Emulators in my CI with acceptable speed.
|
||||
|
||||
## To Use
|
||||
The images are published [here on DockerHub](https://hub.docker.com/repository/docker/fknives/android-test-img).
|
||||
|
||||
> The Images are not yet tested, I consider testing them in the future (once I figure out how).
|
||||
|
||||
To use as a base image: `FROM fknives/android-test-img:<version>`
|
||||
|
||||
To use in CI: `image: fknives/android-test-img:1.0.0`
|
||||
|
||||
*Feel free to use or suggest alternatives.*
|
||||
|
||||
## To Generate
|
||||
|
||||
To generate the Java program is used. (For ease of maintenance, since Kotlin is the language I use the most)
|
||||
To run I use the following command: `./gradlew run --args="$(pwd)/configuration.toml`
|
||||
|
||||
A standalone JAR could be generated if needed.
|
||||
|
||||
> Note: GitHub Action is created to run the script.
|
||||
|
||||
## Contains
|
||||
The Generated images contain Android BuildTools, SDKs, Gradle for easy Android builds and creation of Emulators.
|
||||
|
||||
It contains a `androidemulatorstart` script, which based on the `$EMULATOR_API_LEVEL` ENV variable creates an Emulator then boots it.
|
||||
The script can be found [here](./main/src/main/resources/startemulator).
|
||||
|
||||
## Configuration
|
||||
There are a couple of things to configure in the images, described here with the format and example:
|
||||
```toml
|
||||
# UPDATE: build tools versions can be found via sdkmanager --list and filtering it. Example: `sdkmanager --list | grep -o "build-tools;[0-9.][0-9.]*" | sort | uniq`
|
||||
# build tools installed into every image
|
||||
buildTools = ["33.0.0", "32.0.0", "31.0.0", "30.0.3", "30.0.2"]
|
||||
# UPDATE: sdk versions can be found via sdkmanager --list and filtering it. Example: `sdkmanager --list | grep -o "android-[0-9.][0-9.]*" | sort | uniq`
|
||||
# sdks installed into every image
|
||||
sdks = [30, 31, 32, 33]
|
||||
# UPDATE: latest command lines version here: https://developer.android.com/studio#command-tools
|
||||
androidCommandlineTools = "8512546_latest"
|
||||
# UPDATE: gradle version can be found in projects gradle-wrapper.properties
|
||||
gradleVersion = "7.3.3"
|
||||
# folder to save generated images into
|
||||
output = "outputs"
|
||||
|
||||
# additional variations
|
||||
[variations]
|
||||
# apiLevel variation create separate image with emulator sdk installed and emulator created
|
||||
# this will create two additional images like fknives/android-test-img:1.0.0-api-21, fknives/android-test-img:1.0.0-api-25
|
||||
# This images depend on fknives/android-test-img:1.0.0 and add an additional SDK and create the emulator itself.
|
||||
apiLevels = [21,25]
|
||||
|
||||
# the docker image description
|
||||
# <repository>/<namespace>:<tagPrefix><variationtag>
|
||||
# Example: fknives/android-test-img:1.0.0, fknives/android-test-img:1.0.0-api-21
|
||||
[image]
|
||||
repository = "fknives"
|
||||
namespace = "android-test-img"
|
||||
tagPrefix = "1.0.0"
|
||||
```
|
||||
|
||||
## Example Dockerfile
|
||||
<details>
|
||||
<summary>Dockerfile</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM openjdk:11-jdk-slim
|
||||
# Generated on 2022-08-25
|
||||
|
||||
# installing usual required tools
|
||||
# build-essential, ruby and bundler is needed for fastlane
|
||||
RUN apt-get update && apt-get install -y \
|
||||
sudo \
|
||||
wget \
|
||||
curl \
|
||||
unzip \
|
||||
android-sdk \
|
||||
build-essential \
|
||||
ruby-full && \
|
||||
gem install bundler
|
||||
|
||||
# versions
|
||||
# latest command lines version here: https://developer.android.com/studio#command-tools
|
||||
ENV ANDROID_COMMAND_LINES_TOOLS_VERSION "8512546_latest"
|
||||
# gradle version can be found in projects gradle-wrapper.properties
|
||||
ENV GRADLE_VERSION "7.3.3"
|
||||
|
||||
# set homes and paths
|
||||
ENV ANDROID_HOME "/usr/lib/android-sdk"
|
||||
ENV ANDROID_SDK_ROOT $ANDROID_HOME
|
||||
ENV CMDLINE_TOOLS_ROOT "${ANDROID_HOME}/cmdline-tools/latest/bin"
|
||||
ENV AVD_HOME "/root/.android/avd"
|
||||
ENV PATH "$ANDROID_HOME/cmdline-tools/latest/bin:${PATH}"
|
||||
ENV PATH "$ANDROID_HOME/emulator:${PATH}"
|
||||
ENV PATH "/usr/local/gradle-${GRADLE_VERSION}/bin:${PATH}"
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
# install gradle
|
||||
RUN curl -sSL -o /tmp/gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip && \
|
||||
unzip -d /usr/local /tmp/gradle.zip && \
|
||||
rm -rf /tmp/gradle.zip && \
|
||||
echo "1" | gradle init && \
|
||||
/root/gradlew && \
|
||||
rm -rf /root/* && \
|
||||
rm /root/.gitignore
|
||||
|
||||
# install command line tools
|
||||
RUN wget https://dl.google.com/android/repository/commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
unzip commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip -d cmdline-tools && \
|
||||
rm commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
mv cmdline-tools/cmdline-tools/ cmdline-tools/latest && \
|
||||
mv cmdline-tools $ANDROID_HOME/
|
||||
|
||||
# install emulator and setup sdkmanager
|
||||
RUN yes | sdkmanager --licenses > /dev/null && \
|
||||
sdkmanager --install emulator --channel=0 > /dev/null
|
||||
|
||||
# support libraries and google play services
|
||||
RUN echo y | sdkmanager "extras;android;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;google_play_services"
|
||||
|
||||
# build tools versions and sdk versions can be found via sdkmanager --list and filtering it
|
||||
# install build-tools
|
||||
# example: `echo y | sdkmanager "build-tools;33.0.0"`
|
||||
RUN echo y | sdkmanager "build-tools;33.0.0" && \
|
||||
echo y | sdkmanager "build-tools;32.0.0" && \
|
||||
echo y | sdkmanager "build-tools;31.0.0" && \
|
||||
echo y | sdkmanager "build-tools;30.0.3" && \
|
||||
echo y | sdkmanager "build-tools;30.0.2"
|
||||
|
||||
# install sdks
|
||||
# example: `echo y | sdkmanager "platforms;android-33"`
|
||||
RUN echo y | sdkmanager "platforms;android-30" && \
|
||||
echo y | sdkmanager "platforms;android-31" && \
|
||||
echo y | sdkmanager "platforms;android-32" && \
|
||||
echo y | sdkmanager "platforms;android-33"
|
||||
|
||||
# copy script to install sdk, create emulator and boot it
|
||||
COPY ./startemulator /usr/local/bin/androidemulatorstart
|
||||
RUN chmod 744 /usr/local/bin/androidemulatorstart
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
```
|
||||
</details>
|
||||
|
||||
## License
|
||||
[License file](./LICENSE)
|
||||
10
build.gradle.kts
Normal file
10
build.gradle.kts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.jvm").version("1.6.21").apply(false)
|
||||
}
|
||||
|
||||
tasks {
|
||||
register<Delete>("clean"){
|
||||
delete(rootProject.buildDir)
|
||||
}
|
||||
}
|
||||
25
configuration.toml
Normal file
25
configuration.toml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# UPDATE: build tools versions can be found via sdkmanager --list and filtering it. Example: `sdkmanager --list | grep -o "build-tools;[0-9.][0-9.]*" | sort | uniq`
|
||||
# build tools installed into every image
|
||||
buildTools = ["33.0.0", "32.0.0", "31.0.0", "30.0.3", "30.0.2"]
|
||||
# UPDATE: sdk versions can be found via sdkmanager --list and filtering it. Example: `sdkmanager --list | grep -o "android-[0-9.][0-9.]*" | sort | uniq`
|
||||
# sdks installed into every image
|
||||
sdks = [30, 31, 32, 33]
|
||||
# UPDATE: latest command lines version here: https://developer.android.com/studio#command-tools
|
||||
androidCommandlineTools = "8512546_latest"
|
||||
# UPDATE: gradle version can be found in projects gradle-wrapper.properties
|
||||
gradleVersion = "7.3.3"
|
||||
# folder to save generated images into
|
||||
output = "outputs-1.0.0"
|
||||
|
||||
# additional variations
|
||||
[variations]
|
||||
# apiLevel variation create separate image with emulator sdk installed and emulator created
|
||||
apiLevels = []
|
||||
|
||||
# the docker image description
|
||||
# <repository>/<namespace>:<tagPrefix><variationtag>
|
||||
# Example: fknives/android-test-img:1.0.0, fknives/android-test-img:1.0.0-api-21
|
||||
[image]
|
||||
repository = "fknives"
|
||||
namespace = "android-test-img"
|
||||
tagPrefix = "1.0.0"
|
||||
11
gradle.properties
Normal file
11
gradle.properties
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#Tue Aug 23 17:18:09 EEST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
185
gradlew
vendored
Executable file
185
gradlew
vendored
Executable file
|
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
1
main/.gitignore
vendored
Normal file
1
main/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
31
main/build.gradle.kts
Normal file
31
main/build.gradle.kts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
plugins {
|
||||
id("org.jetbrains.kotlin.jvm")
|
||||
id("application")
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("org.fnives.android.test.dockerfile.Main")
|
||||
}
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
setEvents(setOf("started", "passed", "skipped", "failed"))
|
||||
setExceptionFormat("full")
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val tomlParserVersion = "1.1.0"
|
||||
implementation("cc.ekblad:4koma:$tomlParserVersion")
|
||||
|
||||
val testingJunitJupiterVersion = "5.7.0"
|
||||
val testingJunitJupiterRuntimeVersion = "5.3.1"
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:$testingJunitJupiterVersion")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$testingJunitJupiterRuntimeVersion")
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package org.fnives.android.test.dockerfile
|
||||
|
||||
import org.fnives.android.test.dockerfile.configuration.Configuration
|
||||
import org.fnives.android.test.dockerfile.configuration.ImageConfiguration
|
||||
import org.fnives.android.test.dockerfile.di.ServiceLocatorHolder.ServiceLocator
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Step into the Script.
|
||||
*
|
||||
* This script is intended to:
|
||||
* - Generate Dockerfiles from the given configuration.
|
||||
* - Create DockerImages from the generated Dockerfiles
|
||||
* - Push those images up to DockerHub based on the configucation.
|
||||
*
|
||||
* Needs a single argument to the path of the configuration.toml file.
|
||||
*
|
||||
* Usually run like: ./gradlew run --args="$(pwd)/configuration.toml
|
||||
*
|
||||
* **Docker needs to be installed**
|
||||
*/
|
||||
object Main {
|
||||
|
||||
@JvmStatic
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun main(args: Array<String>) {
|
||||
val configuration = getConfigurationFromArgument(args)
|
||||
|
||||
val configuredServiceLocator = ServiceLocator.loadConfiguration(configuration)
|
||||
val imageConfigurations = listOf(ImageConfiguration(configuration))
|
||||
.plus(configuration.apiLevelVariations.map(::ImageConfiguration))
|
||||
|
||||
configuredServiceLocator.copyScript().invoke()
|
||||
imageConfigurations.map(configuredServiceLocator.contentGenerator()::create)
|
||||
.map(configuredServiceLocator.imageWriter()::write)
|
||||
.forEach(configuredServiceLocator.imageBuildAndPush()::buildPushClean)
|
||||
}
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
private fun getConfigurationFromArgument(args: Array<String>): Configuration {
|
||||
val configFile = getFileFromArgument(args)
|
||||
|
||||
return try {
|
||||
configFile.inputStream().use(ServiceLocator.parser()::invoke)
|
||||
} catch (illegalArgumentException: IllegalArgumentException) {
|
||||
throw IllegalArgumentException("Unparseable file at given path: ${configFile.absolutePath}", illegalArgumentException)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
private fun getFileFromArgument(args: Array<String>): File {
|
||||
require(args.isNotEmpty()) {
|
||||
"Required argument is missing! Required argument is the config.toml file path and just that!"
|
||||
}
|
||||
require(args.size < 2) {
|
||||
"More arguments than expected! Required argument is the config.toml file path and just that!"
|
||||
}
|
||||
val configFile = File(args[0])
|
||||
require(configFile.exists()) {
|
||||
"Can't find file at given path: ${configFile.absolutePath}"
|
||||
}
|
||||
|
||||
return configFile
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package org.fnives.android.test.dockerfile.configuration
|
||||
|
||||
import org.fnives.android.test.dockerfile.generator.DockerFileContent
|
||||
|
||||
/**
|
||||
* Describes all the configurations which can be set inside the [toml][https://toml.io/en/] file.
|
||||
*
|
||||
* @param buildTools android build tools installed into the DockerImages
|
||||
* @param sdks AndroidSDKs installed into the DockerImages
|
||||
* @param commandlineTools AndroidCommandLinesTools installed into the DockerImages
|
||||
* @param gradleVersion GradleVersion to be installed into the DockerImages
|
||||
* @param output the folder which will contain the generated Dockerfiles
|
||||
* @param dockerRepository the repository in DockerHub to push into.
|
||||
* @param dockerNamespace the namespace inside the [dockerRepository] defined on DockerHub.
|
||||
* @param dockerTagPrefix the prefix which should be added to all DockerImage tags when creating [DockerFileContent]
|
||||
* @param apiLevelVariations the specific api levels that should have their own image. This is used so everything is ready for the Emulator for a specific API version.
|
||||
*/
|
||||
data class Configuration(
|
||||
val buildTools: Set<String>,
|
||||
val sdks: Set<Int>,
|
||||
val commandlineTools: String,
|
||||
val gradleVersion: String,
|
||||
val output: String,
|
||||
val dockerRepository: String,
|
||||
val dockerNamespace: String,
|
||||
val dockerTagPrefix: String,
|
||||
val apiLevelVariations: Set<Int> = emptySet()
|
||||
)
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package org.fnives.android.test.dockerfile.configuration
|
||||
|
||||
import cc.ekblad.toml.TomlMapper
|
||||
import cc.ekblad.toml.decode
|
||||
import cc.ekblad.toml.tomlMapper
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Parses the given [toml][https://toml.io/en/] file into a [Configuration] to be usable by the other parts of the app.
|
||||
*/
|
||||
class ConfigurationParser(private val mapper: TomlMapper = tomlMapper {}) {
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun invoke(content: InputStream): Configuration {
|
||||
val raw = try {
|
||||
mapper.decode<RawConfiguration>(content)
|
||||
} catch (cause: Throwable) {
|
||||
throw IllegalArgumentException("Couldn't process configuration", cause)
|
||||
}
|
||||
return raw.convert()
|
||||
}
|
||||
|
||||
private fun RawConfiguration.convert(): Configuration = Configuration(
|
||||
buildTools = buildTools,
|
||||
sdks = sdks,
|
||||
commandlineTools = androidCommandlineTools,
|
||||
gradleVersion = gradleVersion,
|
||||
output = output,
|
||||
apiLevelVariations = variations?.apiLevels.orEmpty(),
|
||||
dockerNamespace = image.namespace,
|
||||
dockerRepository = image.repository,
|
||||
dockerTagPrefix = image.tagPrefix
|
||||
)
|
||||
|
||||
private data class RawConfiguration(
|
||||
val buildTools: Set<String>,
|
||||
val sdks: Set<Int>,
|
||||
val androidCommandlineTools: String,
|
||||
val gradleVersion: String,
|
||||
val output: String,
|
||||
val variations: Variations?,
|
||||
val image: Image
|
||||
) {
|
||||
|
||||
class Variations(
|
||||
val apiLevels: Set<Int> = emptySet()
|
||||
)
|
||||
|
||||
class Image(
|
||||
val namespace: String,
|
||||
val repository: String,
|
||||
val tagPrefix: String,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package org.fnives.android.test.dockerfile.configuration
|
||||
|
||||
/**
|
||||
* Describes the specific that should be used to fill out the Dockerfile templates.
|
||||
*/
|
||||
sealed class ImageConfiguration {
|
||||
data class Generic(
|
||||
val buildTools: Set<String>,
|
||||
val sdks: Set<Int>,
|
||||
val commandlineTools: String,
|
||||
val gradleVersion: String
|
||||
) : ImageConfiguration()
|
||||
|
||||
data class ApiVersion(
|
||||
val version: Int
|
||||
) : ImageConfiguration()
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package org.fnives.android.test.dockerfile.configuration
|
||||
|
||||
/**
|
||||
* Utility function to ease the creation of a generic [ImageConfiguration]
|
||||
*/
|
||||
fun ImageConfiguration(configuration: Configuration): ImageConfiguration =
|
||||
ImageConfiguration.Generic(
|
||||
buildTools = configuration.buildTools,
|
||||
sdks = configuration.sdks,
|
||||
commandlineTools = configuration.commandlineTools,
|
||||
gradleVersion = configuration.gradleVersion,
|
||||
)
|
||||
|
||||
/**
|
||||
* Utility function to ease the creation of a specific [ImageConfiguration], that will depend on a Generic one.
|
||||
*/
|
||||
fun ImageConfiguration(version: Int): ImageConfiguration = ImageConfiguration.ApiVersion(version)
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package org.fnives.android.test.dockerfile.di
|
||||
|
||||
import org.fnives.android.test.dockerfile.generator.DockerFileContentGenerator
|
||||
import org.fnives.android.test.dockerfile.push.DockerBuildAndPush
|
||||
import org.fnives.android.test.dockerfile.write.DockerFileWriter
|
||||
import org.fnives.android.test.dockerfile.write.ScriptCopier
|
||||
|
||||
/**
|
||||
* Gives access to the Services that depend on [Configuration][org.fnives.android.test.dockerfile.configuration.Configuration] and takes care of their creation and instances.
|
||||
*/
|
||||
interface ConfiguredServiceLocator {
|
||||
fun contentGenerator(): DockerFileContentGenerator
|
||||
fun imageWriter(): DockerFileWriter
|
||||
fun imageBuildAndPush(): DockerBuildAndPush
|
||||
fun copyScript(): ScriptCopier
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package org.fnives.android.test.dockerfile.di
|
||||
|
||||
import org.fnives.android.test.dockerfile.configuration.Configuration
|
||||
import org.fnives.android.test.dockerfile.generator.DockerFileContentConfig
|
||||
import org.fnives.android.test.dockerfile.generator.DockerFileContentGenerator
|
||||
import org.fnives.android.test.dockerfile.push.DockerBuildAndPush
|
||||
import org.fnives.android.test.dockerfile.push.PushConfig
|
||||
import org.fnives.android.test.dockerfile.util.resourceFileContent
|
||||
import org.fnives.android.test.dockerfile.util.clock.Clock
|
||||
import org.fnives.android.test.dockerfile.write.DockerFileWriter
|
||||
import org.fnives.android.test.dockerfile.write.ScriptCopier
|
||||
import java.io.File
|
||||
|
||||
/** Syntax Sugar */
|
||||
fun ConfiguredServiceLocator(configuration: Configuration): ConfiguredServiceLocator =
|
||||
DefaultConfiguredServiceLocator(configuration)
|
||||
|
||||
/**
|
||||
* Actual [ConfiguredServiceLocator] keeping references and creating the Services that are dependent on [Configuration].
|
||||
*/
|
||||
class DefaultConfiguredServiceLocator(private val configuration: Configuration) : ConfiguredServiceLocator {
|
||||
private val pushConfig
|
||||
get() = PushConfig(
|
||||
dockerRepository = configuration.dockerRepository,
|
||||
dockerNamespace = configuration.dockerNamespace,
|
||||
output = configuration.output
|
||||
)
|
||||
private val outputFolder get() = File(configuration.output)
|
||||
|
||||
private val processRunner get() = ServiceLocatorHolder.ServiceLocator.processRunner()
|
||||
private val clock: Clock get() = ServiceLocatorHolder.ServiceLocator.clock()
|
||||
private val scriptName: String get() = ServiceLocatorHolder.ServiceLocator.scriptToCopy()
|
||||
|
||||
private val imageBuildAndPush by lazy { DockerBuildAndPush(config = pushConfig, processRunner = processRunner) }
|
||||
private val imageWriter by lazy { DockerFileWriter(outputFolder = outputFolder) }
|
||||
private val copyScript by lazy { ScriptCopier(outputFolder = outputFolder, scriptName = scriptName) }
|
||||
|
||||
private val dockerFileContentConfig
|
||||
get() = DockerFileContentConfig(
|
||||
templateGeneric = resourceFileContent("Dockerfile.template"),
|
||||
templateSpecific = resourceFileContent("Dockerfile.api.template"),
|
||||
dockerRepository = configuration.dockerRepository,
|
||||
dockerNamespace = configuration.dockerNamespace,
|
||||
dockerTagPrefix = configuration.dockerTagPrefix,
|
||||
)
|
||||
|
||||
private val contentGenerator by lazy {
|
||||
DockerFileContentGenerator(
|
||||
config = dockerFileContentConfig,
|
||||
clock = clock
|
||||
)
|
||||
}
|
||||
|
||||
override fun contentGenerator(): DockerFileContentGenerator = contentGenerator
|
||||
|
||||
override fun imageWriter(): DockerFileWriter = imageWriter
|
||||
|
||||
override fun imageBuildAndPush(): DockerBuildAndPush = imageBuildAndPush
|
||||
|
||||
override fun copyScript(): ScriptCopier = copyScript
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package org.fnives.android.test.dockerfile.di
|
||||
|
||||
import org.fnives.android.test.dockerfile.configuration.Configuration
|
||||
import org.fnives.android.test.dockerfile.configuration.ConfigurationParser
|
||||
import org.fnives.android.test.dockerfile.push.ProcessRunner
|
||||
import org.fnives.android.test.dockerfile.push.SystemProcessRunner
|
||||
import org.fnives.android.test.dockerfile.util.clock.Clock
|
||||
import org.fnives.android.test.dockerfile.util.clock.SystemCock
|
||||
|
||||
/**
|
||||
* Actual [ServiceLocator] keeping references and creating the Services.
|
||||
*/
|
||||
object DefaultServiceLocator : ServiceLocator {
|
||||
|
||||
private val parser by lazy { ConfigurationParser() }
|
||||
private val processRunner by lazy { SystemProcessRunner() }
|
||||
|
||||
override fun loadConfiguration(configuration: Configuration): ConfiguredServiceLocator =
|
||||
ConfiguredServiceLocator(configuration)
|
||||
|
||||
override fun parser(): ConfigurationParser = parser
|
||||
|
||||
override fun clock(): Clock = SystemCock
|
||||
|
||||
override fun scriptToCopy(): String = "startemulator"
|
||||
|
||||
override fun processRunner(): ProcessRunner = processRunner
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package org.fnives.android.test.dockerfile.di
|
||||
|
||||
import org.fnives.android.test.dockerfile.configuration.Configuration
|
||||
import org.fnives.android.test.dockerfile.configuration.ConfigurationParser
|
||||
import org.fnives.android.test.dockerfile.push.ProcessRunner
|
||||
import org.fnives.android.test.dockerfile.util.clock.Clock
|
||||
|
||||
/**
|
||||
* Gives access to the Services and takes care of their creation and instances.
|
||||
*/
|
||||
interface ServiceLocator {
|
||||
fun parser(): ConfigurationParser
|
||||
fun clock(): Clock
|
||||
fun scriptToCopy(): String
|
||||
fun processRunner(): ProcessRunner
|
||||
fun loadConfiguration(configuration: Configuration): ConfiguredServiceLocator
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.fnives.android.test.dockerfile.di
|
||||
|
||||
/**
|
||||
* Access point to [ServiceLocator][org.fnives.android.test.dockerfile.di.ServiceLocator] so it can be [swap]ed out in tests.
|
||||
*/
|
||||
object ServiceLocatorHolder {
|
||||
|
||||
/**
|
||||
* Syntax Sugar
|
||||
*/
|
||||
val ServiceLocator get() = get()
|
||||
|
||||
val default: ServiceLocator = DefaultServiceLocator
|
||||
private var actual: ServiceLocator = default
|
||||
|
||||
/**
|
||||
* Change the [ServiceLocator][org.fnives.android.test.dockerfile.di.ServiceLocator] returned in [get] and used.
|
||||
*/
|
||||
fun swap(serviceLocator: ServiceLocator) {
|
||||
actual = serviceLocator
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset back to [default] [ServiceLocator][org.fnives.android.test.dockerfile.di.ServiceLocator] to be returned in [get]
|
||||
*/
|
||||
fun reset() = swap(default)
|
||||
|
||||
/**
|
||||
* Returns the actual [ServiceLocator][org.fnives.android.test.dockerfile.di.ServiceLocator]
|
||||
*/
|
||||
fun get() = actual
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.fnives.android.test.dockerfile.generator
|
||||
|
||||
/**
|
||||
* Describes the content and necessary information to create a Dockerfile and DockerImage from that.
|
||||
*
|
||||
* @param name the name of the Dockerfile to be created
|
||||
* @param tag the id of the DockerImage to be created
|
||||
* @param content the commands which should be in the Dockerfile
|
||||
*/
|
||||
data class DockerFileContent(
|
||||
val name: String,
|
||||
val tag: String,
|
||||
val content: String
|
||||
)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package org.fnives.android.test.dockerfile.generator
|
||||
|
||||
/**
|
||||
* Configuration for the [DockerFileContentGenerator]
|
||||
*
|
||||
* @param templateGeneric the Dockerfile template content for a Generic image
|
||||
* @param templateSpecific the Dockerfile template content for a Specific image, which extends the Generic and adds additional installs.
|
||||
* @param dockerRepository the repository in DockerHub to push into.
|
||||
* @param dockerNamespace the namespace inside the [dockerRepository] defined on DockerHub.
|
||||
* @param dockerTagPrefix the prefix which should be added to all DockerImage tags when creating [DockerFileContent]
|
||||
*/
|
||||
data class DockerFileContentConfig(
|
||||
val templateGeneric: String,
|
||||
val templateSpecific: String,
|
||||
val dockerRepository: String,
|
||||
val dockerNamespace: String,
|
||||
val dockerTagPrefix: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package org.fnives.android.test.dockerfile.generator
|
||||
|
||||
import org.fnives.android.test.dockerfile.configuration.ImageConfiguration
|
||||
import org.fnives.android.test.dockerfile.util.clock.Clock
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Creates [DockerFileContent] from an [ImageConfiguration] based on the [DockerFileContentConfig].
|
||||
*
|
||||
* Uses the templates of [DockerFileContentConfig] and replaces the specifics described in the [ImageConfiguration].
|
||||
* The templates have keywords which are used to replace with the specific commands defined by [ImageConfiguration]
|
||||
*/
|
||||
class DockerFileContentGenerator(
|
||||
private val config: DockerFileContentConfig,
|
||||
private val clock: Clock
|
||||
) {
|
||||
|
||||
fun create(imageConfiguration: ImageConfiguration): DockerFileContent {
|
||||
val dockerfileName = dockerFileName(imageConfiguration.version())
|
||||
val tag = "${config.dockerRepository}/${config.dockerNamespace}:${config.dockerTagPrefix}${tagSuffix(imageConfiguration.version())}"
|
||||
val genericTag = "${config.dockerRepository}/${config.dockerNamespace}:${config.dockerTagPrefix}${tagSuffix(null)}"
|
||||
|
||||
val content = when (imageConfiguration) {
|
||||
is ImageConfiguration.Generic ->
|
||||
config.templateGeneric.setCommandLineToolsVersion(imageConfiguration.commandlineTools)
|
||||
.setGradleVersion(imageConfiguration.gradleVersion)
|
||||
.setInstalledSDKs(imageConfiguration.sdks)
|
||||
.setInstalledBuildTools(imageConfiguration.buildTools)
|
||||
.setDateStamp()
|
||||
is ImageConfiguration.ApiVersion ->
|
||||
config.templateSpecific.setFromTag(genericTag)
|
||||
.setENVAPILevel(imageConfiguration.version)
|
||||
.setDateStamp()
|
||||
}
|
||||
|
||||
return DockerFileContent(
|
||||
name = dockerfileName,
|
||||
content = content,
|
||||
tag = tag
|
||||
)
|
||||
}
|
||||
|
||||
private fun String.setCommandLineToolsVersion(version: String): String =
|
||||
replace(COMMAND_LINES_KEY, version)
|
||||
|
||||
private fun String.setGradleVersion(version: String): String =
|
||||
replace(GRADLE_VERSION_KEY, version)
|
||||
|
||||
private fun String.setInstalledSDKs(sdks: Set<Int>): String {
|
||||
val sdkInstallCommand = sdks.joinToString(" && \\\n ") { getSdkInstallCommand(it) }
|
||||
|
||||
return replace(SDK_INSTALL_COMMANDS_KEY, sdkInstallCommand)
|
||||
}
|
||||
|
||||
private fun String.setInstalledBuildTools(buildTools: Set<String>): String {
|
||||
val buildToolsInstallCommand = buildTools.joinToString(" && \\\n ") { getBuildToolsInstallCommand(it) }
|
||||
|
||||
return replace(BUILD_TOOLS_INSTALL_COMMANDS_KEY, buildToolsInstallCommand)
|
||||
}
|
||||
|
||||
private fun dockerFileName(version: Int?): String =
|
||||
if (version == null) {
|
||||
"Dockerfile"
|
||||
} else {
|
||||
"Dockerfile-api-$version"
|
||||
}
|
||||
|
||||
private fun tagSuffix(version: Int?): String = if (version == null) "" else "-api-$version"
|
||||
|
||||
private fun String.setFromTag(tagPrefix: String): String {
|
||||
val tag = tagPrefix + tagSuffix(null)
|
||||
|
||||
return replace(TAG_KEY, tag)
|
||||
}
|
||||
|
||||
private fun String.setENVAPILevel(apiLevel: Int): String = replace(SET_API_LEVEL, apiLevel.toString())
|
||||
|
||||
private fun String.setDateStamp(): String {
|
||||
val dateStamp = SimpleDateFormat("yyyy-MM-dd").format(Date(clock.currentTimeMillis()))
|
||||
|
||||
return replace(DATE_STAMP_KEY, "Generated on $dateStamp")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val COMMAND_LINES_KEY = "{{COMMAND_LINE_TOOLS}}"
|
||||
private const val GRADLE_VERSION_KEY = "{{GRADLE_VERSION}}"
|
||||
private const val SDK_INSTALL_COMMANDS_KEY = "{{SDK_INSTALL_COMMANDS}}"
|
||||
private const val BUILD_TOOLS_INSTALL_COMMANDS_KEY = "{{BUILD_TOOLS_INSTALL_COMMANDS}}"
|
||||
private const val TAG_KEY = "{{IMG-TAG}}"
|
||||
private const val SET_API_LEVEL = "{{API_LEVEL}}"
|
||||
private const val DATE_STAMP_KEY = "{{GENERATED_AT}}"
|
||||
|
||||
private fun getSdkInstallCommand(sdk: Int) = "echo y | sdkmanager \"platforms;android-$sdk\""
|
||||
|
||||
private fun getBuildToolsInstallCommand(buildTool: String) = "echo y | sdkmanager \"build-tools;$buildTool\""
|
||||
|
||||
private fun ImageConfiguration.version() = when (this) {
|
||||
is ImageConfiguration.ApiVersion -> version
|
||||
is ImageConfiguration.Generic -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package org.fnives.android.test.dockerfile.push
|
||||
|
||||
import org.fnives.android.test.dockerfile.write.DockerFileDescriptor
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Runs the necessary docker commands to create, push and cleanup the DockerImage from the given Dockerfile.
|
||||
*/
|
||||
class DockerBuildAndPush(
|
||||
private val config: PushConfig,
|
||||
private val processRunner: ProcessRunner
|
||||
) {
|
||||
|
||||
private val workDir get() = File(config.output)
|
||||
|
||||
fun build(dockerFileDescriptor: DockerFileDescriptor): String {
|
||||
"docker build -t ${dockerFileDescriptor.tag} -f ${dockerFileDescriptor.filePath} .".runCommand(workDir)
|
||||
|
||||
return dockerFileDescriptor.tag
|
||||
}
|
||||
|
||||
fun push(id: String) {
|
||||
"docker push $id".runCommand(workDir)
|
||||
}
|
||||
|
||||
fun removeLocalImage(id: String) {
|
||||
"docker image rm -f $id".runCommand(workDir)
|
||||
}
|
||||
|
||||
fun buildPushClean(dockerFileDescriptor: DockerFileDescriptor) {
|
||||
val id = build(dockerFileDescriptor)
|
||||
push(id)
|
||||
removeLocalImage(id)
|
||||
}
|
||||
|
||||
private fun String.runCommand(workingDir: File) =
|
||||
processRunner.run(workingDirectory = workingDir, command = this)
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package org.fnives.android.test.dockerfile.push
|
||||
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
/**
|
||||
* Process Runner is able to run a given command in the System process.
|
||||
*/
|
||||
interface ProcessRunner {
|
||||
|
||||
/**
|
||||
* Runs the given [command] as a System process in [workingDirectory].
|
||||
*/
|
||||
@Throws(TimeoutException::class)
|
||||
fun run(workingDirectory: File, command: String)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.fnives.android.test.dockerfile.push
|
||||
|
||||
/**
|
||||
* Configuration for [DockerBuildAndPush].
|
||||
*
|
||||
* @param dockerRepository the repository in DockerHub to push into.
|
||||
* @param dockerNamespace the namespace inside the [dockerRepository] defined on DockerHub.
|
||||
* @param output the path to the output folder.
|
||||
*/
|
||||
data class PushConfig(
|
||||
val dockerRepository: String,
|
||||
val dockerNamespace: String,
|
||||
val output: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package org.fnives.android.test.dockerfile.push
|
||||
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
/**
|
||||
* Actual [ProcessRunner] which converts the command into the Process then starts and awaits it.
|
||||
*/
|
||||
class SystemProcessRunner(
|
||||
private val timeout: Long = 60,
|
||||
private val timeoutUnit: TimeUnit = TimeUnit.MINUTES
|
||||
) : ProcessRunner {
|
||||
|
||||
override fun run(workingDirectory: File, command: String) {
|
||||
val result = ProcessBuilder(*command.split(" ").toTypedArray())
|
||||
.directory(workingDirectory)
|
||||
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
|
||||
.redirectError(ProcessBuilder.Redirect.INHERIT)
|
||||
.start()
|
||||
.waitFor(timeout, timeoutUnit)
|
||||
if (!result) {
|
||||
throw TimeoutException("The command have not finished in $timeout ${timeoutUnit}! `$command`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package org.fnives.android.test.dockerfile.util
|
||||
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
|
||||
/**
|
||||
* Helper function to read contents of files baked into the resources folder
|
||||
*/
|
||||
internal fun Any.resourceFileContent(filePath: String): String = try {
|
||||
BufferedReader(InputStreamReader(resourceFileStream(filePath)))
|
||||
.readLines().joinToString("\n")
|
||||
} catch (nullPointerException: NullPointerException) {
|
||||
throw IllegalArgumentException("$filePath file not found!", nullPointerException)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to access contents of files baked into the resources folder
|
||||
*/
|
||||
internal fun Any.resourceFileStream(filePath: String): InputStream = try {
|
||||
this.javaClass.classLoader.getResourceAsStream(filePath)!!
|
||||
} catch (nullPointerException: NullPointerException) {
|
||||
throw IllegalArgumentException("$filePath file not found!", nullPointerException)
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.fnives.android.test.dockerfile.util.clock
|
||||
|
||||
/**
|
||||
* Simple Utility interface providing the [currentTimeMillis].
|
||||
*/
|
||||
interface Clock {
|
||||
|
||||
fun currentTimeMillis(): Long
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.fnives.android.test.dockerfile.util.clock
|
||||
|
||||
/**
|
||||
* Actual [Clock] returning the System time.
|
||||
*/
|
||||
object SystemCock : Clock {
|
||||
|
||||
override fun currentTimeMillis(): Long = System.currentTimeMillis()
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.fnives.android.test.dockerfile.write
|
||||
|
||||
/**
|
||||
* Describes [DockerFileContent][org.fnives.android.test.dockerfile.generator.DockerFileContent] which was written into a file.
|
||||
* @param filePath is the absolut path of the File
|
||||
* @param tag is the id of the DockerImage.
|
||||
*/
|
||||
data class DockerFileDescriptor(val filePath: String, val tag: String)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package org.fnives.android.test.dockerfile.write
|
||||
|
||||
import org.fnives.android.test.dockerfile.generator.DockerFileContent
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Simple configured class which can write a [DockerFileContent] into a file
|
||||
* within the given [outputFolder] configuration, returns [DockerFileDescriptor] describing the written file.
|
||||
*/
|
||||
class DockerFileWriter(private val outputFolder: File) {
|
||||
|
||||
fun write(dockerFile: DockerFileContent): DockerFileDescriptor {
|
||||
outputFolder.mkdirs()
|
||||
|
||||
val file = File(outputFolder, dockerFile.name)
|
||||
file.createNewFile()
|
||||
file.writeText(dockerFile.content)
|
||||
|
||||
return DockerFileDescriptor(filePath = file.absolutePath, tag = dockerFile.tag)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package org.fnives.android.test.dockerfile.write
|
||||
|
||||
import org.fnives.android.test.dockerfile.util.resourceFileStream
|
||||
import java.io.File
|
||||
|
||||
class ScriptCopier(private val outputFolder: File, private val scriptName: String) {
|
||||
|
||||
fun invoke() {
|
||||
outputFolder.mkdirs()
|
||||
val outputScript = File(outputFolder, scriptName)
|
||||
outputScript.createNewFile()
|
||||
outputScript.outputStream().use {
|
||||
resourceFileStream(scriptName).copyTo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
8
main/src/main/resources/Dockerfile.api.template
Normal file
8
main/src/main/resources/Dockerfile.api.template
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
FROM {{IMG-TAG}}
|
||||
# {{GENERATED_AT}}
|
||||
|
||||
ENV EMULATOR_API_LEVEL {{API_LEVEL}}
|
||||
|
||||
RUN /usr/local/bin/androidemulatorstart --buildOnly
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
71
main/src/main/resources/Dockerfile.template
Normal file
71
main/src/main/resources/Dockerfile.template
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
FROM openjdk:11-jdk-slim
|
||||
# {{GENERATED_AT}}
|
||||
|
||||
# installing usual required tools
|
||||
# build-essential, ruby and bundler is needed for fastlane
|
||||
RUN apt-get update && apt-get install -y \
|
||||
sudo \
|
||||
wget \
|
||||
curl \
|
||||
unzip \
|
||||
android-sdk \
|
||||
build-essential \
|
||||
ruby-full && \
|
||||
gem install bundler
|
||||
|
||||
# versions
|
||||
# latest command lines version here: https://developer.android.com/studio#command-tools
|
||||
ENV ANDROID_COMMAND_LINES_TOOLS_VERSION "{{COMMAND_LINE_TOOLS}}"
|
||||
# gradle version can be found in projects gradle-wrapper.properties
|
||||
ENV GRADLE_VERSION "{{GRADLE_VERSION}}"
|
||||
|
||||
# set homes and paths
|
||||
ENV ANDROID_HOME "/usr/lib/android-sdk"
|
||||
ENV ANDROID_SDK_ROOT $ANDROID_HOME
|
||||
ENV CMDLINE_TOOLS_ROOT "${ANDROID_HOME}/cmdline-tools/latest/bin"
|
||||
ENV AVD_HOME "/root/.android/avd"
|
||||
ENV PATH "$ANDROID_HOME/cmdline-tools/latest/bin:${PATH}"
|
||||
ENV PATH "$ANDROID_HOME/emulator:${PATH}"
|
||||
ENV PATH "/usr/local/gradle-${GRADLE_VERSION}/bin:${PATH}"
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
# install gradle
|
||||
RUN curl -sSL -o /tmp/gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip && \
|
||||
unzip -d /usr/local /tmp/gradle.zip && \
|
||||
rm -rf /tmp/gradle.zip && \
|
||||
echo "1" | gradle init && \
|
||||
/root/gradlew && \
|
||||
rm -rf /root/* && \
|
||||
rm /root/.gitignore
|
||||
|
||||
# install command line tools
|
||||
RUN wget https://dl.google.com/android/repository/commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
unzip commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip -d cmdline-tools && \
|
||||
rm commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
mv cmdline-tools/cmdline-tools/ cmdline-tools/latest && \
|
||||
mv cmdline-tools $ANDROID_HOME/
|
||||
|
||||
# install emulator and setup sdkmanager
|
||||
RUN yes | sdkmanager --licenses > /dev/null && \
|
||||
sdkmanager --install emulator --channel=0 > /dev/null
|
||||
|
||||
# support libraries and google play services
|
||||
RUN echo y | sdkmanager "extras;android;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;google_play_services"
|
||||
|
||||
# build tools versions and sdk versions can be found via sdkmanager --list and filtering it
|
||||
# install build-tools
|
||||
# example: `echo y | sdkmanager "build-tools;33.0.0"`
|
||||
RUN {{BUILD_TOOLS_INSTALL_COMMANDS}}
|
||||
|
||||
# install sdks
|
||||
# example: `echo y | sdkmanager "platforms;android-33"`
|
||||
RUN {{SDK_INSTALL_COMMANDS}}
|
||||
|
||||
# copy script to install sdk, create emulator and boot it
|
||||
COPY ./startemulator /usr/local/bin/androidemulatorstart
|
||||
RUN chmod 744 /usr/local/bin/androidemulatorstart
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
105
main/src/main/resources/startemulator
Normal file
105
main/src/main/resources/startemulator
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ "$1" == "--help" ]]
|
||||
then
|
||||
echo "Creates default emulator and starts it."
|
||||
echo "API level and Target can be customized, for more customization use the avdmanager directly!"
|
||||
echo "API_level is read from EMULATOR_API_LEVEL variable"
|
||||
echo "target is read from EMULATOR_TARGET variable or defaults to target"
|
||||
echo "--buildOnly parameter means the emulator will be created, but not started"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
if [[ -z $EMULATOR_API_LEVEL ]]
|
||||
then
|
||||
echo "EMULATOR_API_LEVEL not set!" >&2
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if [[ -z $EMULATOR_TARGET ]]
|
||||
then
|
||||
if [[ $EMULATOR_API_LEVEL -ge 32 ]]
|
||||
then
|
||||
echo "EMULATOR_TARGET not set using \"google_apis\", \"default\" doesn't exists above 32"
|
||||
EMULATOR_TARGET="google_apis"
|
||||
else
|
||||
echo "EMULATOR_TARGET not set using \"default\"" >&2
|
||||
EMULATOR_TARGET="default"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z $AVD_HOME ]]
|
||||
then
|
||||
AVD_HOME="$HOME/.android/avd"
|
||||
echo "AVD_HOME not set, using fallback: $AVD_HOME"
|
||||
fi
|
||||
|
||||
if [[ "$1" == "--buildOnly" ]]
|
||||
then
|
||||
echo "INFO: --buildOnly found, emulator will be created but not started!" >&2
|
||||
fi
|
||||
|
||||
echo "INFO: installing sdk" >&2
|
||||
sdkmanager --install "system-images;android-$EMULATOR_API_LEVEL;$EMULATOR_TARGET;x86_64" --channel=0 > /dev/null 2> /dev/null
|
||||
|
||||
|
||||
echo "INFO: creating emulator" >&2
|
||||
EMULATOR_NAME="test_$EMULATOR_API_LEVEL"
|
||||
echo "no" | avdmanager create avd -n "$EMULATOR_NAME" --abi "$EMULATOR_TARGET/x86_64" --package "system-images;android-$EMULATOR_API_LEVEL;$EMULATOR_TARGET;x86_64"
|
||||
|
||||
echo "INFO: config of emulator:" >&2
|
||||
cat $AVD_HOME/$EMULATOR_NAME.avd/config.ini >&2
|
||||
echo "INFO: modifying config of emulator..." >&2
|
||||
echo 'disk.dataPartition.size=2G\n' >> $AVD_HOME/$EMULATOR_NAME.avd/config.ini
|
||||
|
||||
if [[ "$1" == "--buildOnly" ]]
|
||||
then
|
||||
echo "INFO: emulator created!" >&2
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
echo "INFO: checking devices, expecting nothing..." >&2
|
||||
adb devices
|
||||
|
||||
echo "INFO: starting emulator..." >&2
|
||||
emulator -avd $EMULATOR_NAME -no-snapshot-save -no-window -gpu off -noaudio -no-boot-anim -camera-back none > emulator.log &
|
||||
|
||||
echo "INFO: waiting for emulator..." >&2
|
||||
sleep 30s
|
||||
|
||||
echo "INFO: emulator logs" >&2
|
||||
cat emulator.log
|
||||
|
||||
echo "INFO: checking devices..." >&2
|
||||
adb devices
|
||||
echo "INFO: waiting for emulator to be booted..." >&2
|
||||
BOOT_WAIT_LOG_MESSAGE="INFO: still waiting for emulator to boot"
|
||||
SECONDS_PASSED=0;
|
||||
BOOT_COMPLETE=`adb shell getprop sys.boot_completed`
|
||||
while [[ -z $BOOT_COMPLETE ]];
|
||||
do
|
||||
sleep $BOOT_DELAY_IN_SECONDS;
|
||||
SECONDS_PASSED=$(( $SECONDS_PASSED+$BOOT_DELAY_IN_SECONDS ));
|
||||
echo "INFO: still waiting for emulator to boot ($SECONDS_PASSED seconds passed)"; >&2
|
||||
BOOT_COMPLETE=`adb shell getprop sys.boot_completed`
|
||||
done;
|
||||
echo "INFO: emulator booted." >&2
|
||||
# alternative: https://raw.githubusercontent.com/travis-ci/travis-cookbooks/0f497eb71291b52a703143c5cd63a217c8766dc9/community-cookbooks/android-sdk/files/default/android-wait-for-emulator
|
||||
|
||||
echo "INFO: clearing animations..." >&2
|
||||
adb shell settings put global window_animation_scale 0.0
|
||||
adb shell settings put global transition_animation_scale 0.0
|
||||
adb shell settings put global animator_duration_scale 0.0
|
||||
|
||||
echo "INFO: waiting for emulator have sdk property..." >&2
|
||||
SDK_VERSION=`adb shell getprop ro.build.version.sdk`
|
||||
while [[ -z $SDK_VERSION ]];
|
||||
do
|
||||
sleep $BOOT_DELAY_IN_SECONDS;
|
||||
echo "INFO: still waiting for emulator to have SDK property"; >&2
|
||||
SDK_VERSION=`adb shell getprop ro.build.version.sdk`
|
||||
done;
|
||||
|
||||
echo "INFO: unlocking screen..." >&2
|
||||
adb shell input keyevent 82
|
||||
echo "INFO: emulator ready!" >&2
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package org.fnives.android.test.dockerfile.configuration
|
||||
|
||||
import org.fnives.android.test.dockerfile.util.resourceFileAsInputStream
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ConfigurationParserTest {
|
||||
|
||||
private lateinit var parser: ConfigurationParser
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
parser = ConfigurationParser()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun example() {
|
||||
val expectedConfiguration = Configuration(
|
||||
buildTools = setOf("33.0.0", "32.0.0", "31.0.0", "30.0.3", "30.0.2"),
|
||||
sdks = setOf(30, 31, 32, 33),
|
||||
commandlineTools = "8512546_latest",
|
||||
gradleVersion = "7.3.3",
|
||||
output = "outputs",
|
||||
dockerRepository = "fknives",
|
||||
dockerNamespace = "android-test-img",
|
||||
dockerTagPrefix = "1.0.0",
|
||||
apiLevelVariations = setOf(21, 22, 23)
|
||||
)
|
||||
val rawConfiguration = resourceFileAsInputStream("example_configuration.toml")
|
||||
|
||||
val actual = parser.invoke(rawConfiguration)
|
||||
|
||||
Assertions.assertEquals(expectedConfiguration, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun singleConfiguration() {
|
||||
val expectedConfiguration = Configuration(
|
||||
buildTools = setOf("33.0.0", "32.0.0", "31.0.0"),
|
||||
sdks = setOf(32, 33),
|
||||
commandlineTools = "8512546_latest",
|
||||
gradleVersion = "7.3.3",
|
||||
output = "outputs",
|
||||
dockerRepository = "fknives",
|
||||
dockerNamespace = "android-test-img",
|
||||
dockerTagPrefix = "1.0.0",
|
||||
apiLevelVariations = emptySet()
|
||||
)
|
||||
val rawConfiguration = resourceFileAsInputStream("single_configuration.toml")
|
||||
|
||||
val actual = parser.invoke(rawConfiguration)
|
||||
|
||||
Assertions.assertEquals(expectedConfiguration, actual)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package org.fnives.android.test.dockerfile.generator
|
||||
|
||||
import org.fnives.android.test.dockerfile.configuration.ImageConfiguration
|
||||
import org.fnives.android.test.dockerfile.util.FixedClock
|
||||
import org.fnives.android.test.dockerfile.util.readResourceFile
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class DockerFileContentGeneratorTest {
|
||||
|
||||
private lateinit var generator: DockerFileContentGenerator
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
generator = DockerFileContentGenerator(
|
||||
config = DockerFileContentConfig(
|
||||
templateGeneric = readResourceFile("Dockerfile.template"),
|
||||
templateSpecific = readResourceFile("Dockerfile.api.template"),
|
||||
dockerRepository = "fknives",
|
||||
dockerTagPrefix = "1.0",
|
||||
dockerNamespace = "ati"
|
||||
),
|
||||
clock = FixedClock(year = 2021, month = 11, dayOfMonth = 15)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun generic() {
|
||||
val expected = DockerFileContent(
|
||||
content = readResourceFile("expected_generic_dockerfile"),
|
||||
name = "Dockerfile",
|
||||
tag = "fknives/ati:1.0"
|
||||
)
|
||||
val config = ImageConfiguration.Generic(
|
||||
buildTools = setOf("30.0.0", "32.0.0", "31.0.0", "30.0.3", "30.0.2"),
|
||||
sdks = setOf(33, 32, 31, 30),
|
||||
commandlineTools = "8512546_latest",
|
||||
gradleVersion = "7.3.3"
|
||||
)
|
||||
|
||||
val actual = generator.create(config)
|
||||
|
||||
Assertions.assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun api27() {
|
||||
val expected = DockerFileContent(
|
||||
content = readResourceFile("expected_api_27_dockerfile"),
|
||||
name = "Dockerfile-api-27",
|
||||
tag = "fknives/ati:1.0-api-27"
|
||||
)
|
||||
val config = ImageConfiguration.ApiVersion(27)
|
||||
|
||||
val actual = generator.create(config)
|
||||
|
||||
Assertions.assertEquals(expected, actual)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package org.fnives.android.test.dockerfile.integration
|
||||
|
||||
import org.fnives.android.test.dockerfile.Main
|
||||
import org.fnives.android.test.dockerfile.di.ServiceLocatorHolder
|
||||
import org.fnives.android.test.dockerfile.util.CapturingProcessRunner
|
||||
import org.fnives.android.test.dockerfile.util.FixedClock
|
||||
import org.fnives.android.test.dockerfile.util.OverrideableClockServiceLocator
|
||||
import org.fnives.android.test.dockerfile.util.assertCollectionsEquals
|
||||
import org.fnives.android.test.dockerfile.util.readResourceFile
|
||||
import org.fnives.android.test.dockerfile.util.workDirAbsolutePathWithCommands
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.io.File
|
||||
|
||||
class IntegrationTest {
|
||||
|
||||
@TempDir
|
||||
lateinit var tempDir: File
|
||||
|
||||
private lateinit var capturingProcessRunner: CapturingProcessRunner
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
capturingProcessRunner = CapturingProcessRunner()
|
||||
val mockServiceLocator = OverrideableClockServiceLocator()
|
||||
mockServiceLocator.clock = FixedClock(year = 2016, month = 5, dayOfMonth = 28)
|
||||
mockServiceLocator.processRunner = capturingProcessRunner
|
||||
|
||||
ServiceLocatorHolder.swap(mockServiceLocator)
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
ServiceLocatorHolder.reset()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noArgumentsGivesError() {
|
||||
val expectedMessage = "Required argument is missing! Required argument is the config.toml file path and just that!"
|
||||
|
||||
val actual = Assertions.assertThrows(IllegalArgumentException::class.java) {
|
||||
Main.main(emptyArray())
|
||||
}
|
||||
|
||||
Assertions.assertEquals(expectedMessage, actual.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun tooManyArgumentsGivesError() {
|
||||
val expectedMessage = "More arguments than expected! Required argument is the config.toml file path and just that!"
|
||||
|
||||
val actual = Assertions.assertThrows(IllegalArgumentException::class.java) {
|
||||
Main.main(arrayOf("", ""))
|
||||
}
|
||||
|
||||
Assertions.assertEquals(expectedMessage, actual.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nonExistentConfigurationFileGivesError() {
|
||||
val nonExistentFile = File(tempDir, "alma")
|
||||
val actual = Assertions.assertThrows(IllegalArgumentException::class.java) {
|
||||
Main.main(arrayOf(nonExistentFile.absolutePath))
|
||||
}
|
||||
|
||||
Assertions.assertEquals("Can't find file at given path: ${nonExistentFile.absolutePath}", actual.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cannotParseConfigFile() {
|
||||
val file = File(tempDir, "alma")
|
||||
|
||||
file.createNewFile()
|
||||
val actual = Assertions.assertThrows(IllegalArgumentException::class.java) {
|
||||
Main.main(arrayOf(file.absolutePath))
|
||||
}
|
||||
|
||||
Assertions.assertEquals("Unparseable file at given path: ${file.absolutePath}", actual.message)
|
||||
Assertions.assertNotNull(actual.cause)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun singleConfiguration() {
|
||||
val file = File(tempDir, "config.toml")
|
||||
val outputDir = File(tempDir, "output")
|
||||
outputDir.mkdir()
|
||||
val updatedConfiguration = readResourceFile(resourceName = "single_configuration.toml")
|
||||
.replace("output = \"outputs\"", "output = \"${outputDir.absolutePath}\"")
|
||||
file.writeText(updatedConfiguration)
|
||||
|
||||
val expectedDockerCommands = listOf(
|
||||
outputDir.absolutePath to "docker build -t fknives/android-test-img:1.0.0 -f ${File(outputDir, "Dockerfile").absolutePath} .",
|
||||
outputDir.absolutePath to "docker push fknives/android-test-img:1.0.0",
|
||||
outputDir.absolutePath to "docker image rm -f fknives/android-test-img:1.0.0"
|
||||
)
|
||||
val expectedDockerFileContent = readResourceFile("expected_single_configuration.Dockerfile")
|
||||
val expectedScriptContent = readResourceFile("startemulator")
|
||||
|
||||
Main.main(arrayOf(file.absolutePath))
|
||||
val actualDockerfileContent = File(outputDir, "Dockerfile").readText()
|
||||
val actualScriptFileContent = File(outputDir, "startemulator").readText()
|
||||
|
||||
assertCollectionsEquals(expectedDockerCommands, capturingProcessRunner.workDirAbsolutePathWithCommands)
|
||||
Assertions.assertEquals(expectedDockerFileContent, actualDockerfileContent)
|
||||
Assertions.assertEquals(expectedScriptContent, actualScriptFileContent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multiApiConfiguration() {
|
||||
val file = File(tempDir, "config.toml")
|
||||
val outputDir = File(tempDir, "output")
|
||||
outputDir.mkdir()
|
||||
val updatedConfiguration = readResourceFile(resourceName = "multi_api_configuration.toml")
|
||||
.replace("output = \"outputs\"", "output = \"${outputDir.absolutePath}\"")
|
||||
file.writeText(updatedConfiguration)
|
||||
|
||||
val expectedDockerCommands = listOf(
|
||||
outputDir.absolutePath to "docker build -t fknives2/android-test-img-multi:1.0.1 -f ${File(outputDir, "Dockerfile").absolutePath} .",
|
||||
outputDir.absolutePath to "docker push fknives2/android-test-img-multi:1.0.1",
|
||||
outputDir.absolutePath to "docker image rm -f fknives2/android-test-img-multi:1.0.1",
|
||||
outputDir.absolutePath to "docker build -t fknives2/android-test-img-multi:1.0.1-api-22 -f ${File(outputDir, "Dockerfile-api-22").absolutePath} .",
|
||||
outputDir.absolutePath to "docker push fknives2/android-test-img-multi:1.0.1-api-22",
|
||||
outputDir.absolutePath to "docker image rm -f fknives2/android-test-img-multi:1.0.1-api-22",
|
||||
outputDir.absolutePath to "docker build -t fknives2/android-test-img-multi:1.0.1-api-30 -f ${File(outputDir, "Dockerfile-api-30").absolutePath} .",
|
||||
outputDir.absolutePath to "docker push fknives2/android-test-img-multi:1.0.1-api-30",
|
||||
outputDir.absolutePath to "docker image rm -f fknives2/android-test-img-multi:1.0.1-api-30"
|
||||
)
|
||||
val expectedDockerFileContent = readResourceFile("expected_multi_api_configuration.Dockerfile")
|
||||
val expectedDocker22FileContent = readResourceFile("expected_multi_api_configuration.22.Dockerfile")
|
||||
val expectedDocker30FileContent = readResourceFile("expected_multi_api_configuration.30.Dockerfile")
|
||||
val expectedScriptContent = readResourceFile("startemulator")
|
||||
|
||||
Main.main(arrayOf(file.absolutePath))
|
||||
val actualDockerfileContent = File(outputDir, "Dockerfile").readText()
|
||||
val actualDocker22fileContent = File(outputDir, "Dockerfile-api-22").readText()
|
||||
val actualDocker30fileContent = File(outputDir, "Dockerfile-api-30").readText()
|
||||
val actualScriptFileContent = File(outputDir, "startemulator").readText()
|
||||
|
||||
assertCollectionsEquals(expectedDockerCommands, capturingProcessRunner.workDirAbsolutePathWithCommands)
|
||||
Assertions.assertEquals(expectedDockerFileContent, actualDockerfileContent)
|
||||
Assertions.assertEquals(expectedDocker22FileContent, actualDocker22fileContent)
|
||||
Assertions.assertEquals(expectedDocker30FileContent, actualDocker30fileContent)
|
||||
Assertions.assertEquals(expectedScriptContent, actualScriptFileContent)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package org.fnives.android.test.dockerfile.push
|
||||
|
||||
import org.fnives.android.test.dockerfile.util.CapturingProcessRunner
|
||||
import org.fnives.android.test.dockerfile.util.assertCollectionsEquals
|
||||
import org.fnives.android.test.dockerfile.util.workDirAbsolutePathWithCommands
|
||||
import org.fnives.android.test.dockerfile.write.DockerFileDescriptor
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
|
||||
internal class DockerBuildAndPushTest {
|
||||
|
||||
private lateinit var sut: DockerBuildAndPush
|
||||
private lateinit var capturingProcessRunner: CapturingProcessRunner
|
||||
private lateinit var expectedFolder: String
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
capturingProcessRunner = CapturingProcessRunner(
|
||||
defaultReturnValue = true,
|
||||
fallbackToDefault = true
|
||||
)
|
||||
val folderName = "folder"
|
||||
expectedFolder = File(folderName).absolutePath
|
||||
sut = DockerBuildAndPush(
|
||||
config = PushConfig(
|
||||
dockerRepository = "repo",
|
||||
dockerNamespace = "name",
|
||||
output = folderName
|
||||
),
|
||||
processRunner = capturingProcessRunner
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyBuildCommand() {
|
||||
val expected = listOf(expectedFolder to "docker build -t 123 -f /banan/alma .")
|
||||
|
||||
sut.build(DockerFileDescriptor("/banan/alma", "123"))
|
||||
val actual = capturingProcessRunner.workDirAbsolutePathWithCommands
|
||||
|
||||
assertCollectionsEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyPushCommand() {
|
||||
val expected = listOf(expectedFolder to "docker push id")
|
||||
|
||||
sut.push("id")
|
||||
val actual = capturingProcessRunner.workDirAbsolutePathWithCommands
|
||||
|
||||
assertCollectionsEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyCleanCommand() {
|
||||
val expected = listOf(expectedFolder to "docker image rm -f id")
|
||||
|
||||
sut.removeLocalImage("id")
|
||||
val actual = capturingProcessRunner.workDirAbsolutePathWithCommands
|
||||
|
||||
assertCollectionsEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyBuildPushClean() {
|
||||
val expected = listOf(
|
||||
expectedFolder to "docker build -t 123 -f banan/alma .",
|
||||
expectedFolder to "docker push 123",
|
||||
expectedFolder to "docker image rm -f 123"
|
||||
)
|
||||
|
||||
sut.buildPushClean(DockerFileDescriptor("banan/alma", "123"))
|
||||
val actual = capturingProcessRunner.workDirAbsolutePathWithCommands
|
||||
|
||||
assertCollectionsEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyMultipleBuildPushClean() {
|
||||
val expected = listOf(
|
||||
expectedFolder to "docker build -t 123 -f banan/alma .",
|
||||
expectedFolder to "docker push 123",
|
||||
expectedFolder to "docker image rm -f 123",
|
||||
expectedFolder to "docker build -t fknives/421:512.3 -f banan/alma2 .",
|
||||
expectedFolder to "docker push fknives/421:512.3",
|
||||
expectedFolder to "docker image rm -f fknives/421:512.3"
|
||||
)
|
||||
|
||||
sut.buildPushClean(DockerFileDescriptor("banan/alma", "123"))
|
||||
sut.buildPushClean(DockerFileDescriptor("banan/alma2", "fknives/421:512.3"))
|
||||
val actual = capturingProcessRunner.workDirAbsolutePathWithCommands
|
||||
|
||||
assertCollectionsEquals(expected, actual)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package org.fnives.android.test.dockerfile.util
|
||||
|
||||
import org.junit.jupiter.api.Assertions
|
||||
|
||||
inline fun <reified T> assertCollectionsEquals(expected: Collection<T>, actual: Collection<T>) {
|
||||
Assertions.assertArrayEquals(expected.toTypedArray(), actual.toTypedArray())
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package org.fnives.android.test.dockerfile.util
|
||||
|
||||
import org.fnives.android.test.dockerfile.push.ProcessRunner
|
||||
import java.io.File
|
||||
|
||||
class CapturingProcessRunner : ProcessRunner {
|
||||
|
||||
private val _receivedCommands = mutableListOf<Pair<File, String>>()
|
||||
val receivedCommands get() = _receivedCommands.toList()
|
||||
|
||||
override fun run(workingDirectory: File, command: String) {
|
||||
_receivedCommands.add(workingDirectory to command)
|
||||
}
|
||||
}
|
||||
|
||||
val CapturingProcessRunner.workDirAbsolutePathWithCommands: List<Pair<String, String>>
|
||||
get() = receivedCommands.map { (workdir, command) -> workdir.absolutePath to command }
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package org.fnives.android.test.dockerfile.util
|
||||
|
||||
import org.fnives.android.test.dockerfile.util.clock.Clock
|
||||
import java.util.Calendar
|
||||
|
||||
class FixedClock(val timeMillis: Long) : Clock {
|
||||
|
||||
override fun currentTimeMillis(): Long = timeMillis
|
||||
}
|
||||
|
||||
fun FixedClock(year: Int, month: Int, dayOfMonth: Int): Clock {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.set(Calendar.YEAR, year)
|
||||
calendar.set(Calendar.MONTH, month - 1 + Calendar.JANUARY)
|
||||
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
|
||||
|
||||
return FixedClock(calendar.timeInMillis)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.fnives.android.test.dockerfile.util
|
||||
|
||||
import org.fnives.android.test.dockerfile.configuration.Configuration
|
||||
import org.fnives.android.test.dockerfile.configuration.ConfigurationParser
|
||||
import org.fnives.android.test.dockerfile.di.ConfiguredServiceLocator
|
||||
import org.fnives.android.test.dockerfile.di.ServiceLocator
|
||||
import org.fnives.android.test.dockerfile.di.ServiceLocatorHolder
|
||||
import org.fnives.android.test.dockerfile.push.ProcessRunner
|
||||
import org.fnives.android.test.dockerfile.util.clock.Clock
|
||||
|
||||
class OverrideableClockServiceLocator(private val delegate: ServiceLocator = ServiceLocatorHolder.default) : ServiceLocator by delegate {
|
||||
|
||||
var clock: Clock? = null
|
||||
var configuredServiceLocator: ConfiguredServiceLocator? = null
|
||||
var configurationParser: ConfigurationParser? = null
|
||||
var processRunner: ProcessRunner? = null
|
||||
var scriptToCopy: String? = null
|
||||
|
||||
override fun clock(): Clock = clock ?: delegate.clock()
|
||||
|
||||
override fun loadConfiguration(configuration: Configuration): ConfiguredServiceLocator =
|
||||
configuredServiceLocator ?: delegate.loadConfiguration(configuration)
|
||||
|
||||
override fun parser(): ConfigurationParser =
|
||||
configurationParser ?: delegate.parser()
|
||||
|
||||
override fun processRunner(): ProcessRunner =
|
||||
processRunner ?: delegate.processRunner()
|
||||
|
||||
override fun scriptToCopy(): String =
|
||||
scriptToCopy ?: delegate.scriptToCopy()
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.fnives.android.test.dockerfile.util
|
||||
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
|
||||
internal fun Any.readResourceFile(resourceName: String): String = try {
|
||||
BufferedReader(InputStreamReader(resourceFileAsInputStream(resourceName)))
|
||||
.readLines().joinToString("\n")
|
||||
} catch (nullPointerException: NullPointerException) {
|
||||
throw IllegalArgumentException("$resourceName file not found!", nullPointerException)
|
||||
}
|
||||
|
||||
internal fun Any.resourceFileAsInputStream(resourceName: String): InputStream = try {
|
||||
this.javaClass.classLoader.getResourceAsStream(resourceName)!!
|
||||
} catch (nullPointerException: NullPointerException) {
|
||||
throw IllegalArgumentException("$resourceName file not found!", nullPointerException)
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package org.fnives.android.test.dockerfile.write
|
||||
|
||||
import org.fnives.android.test.dockerfile.generator.DockerFileContent
|
||||
import org.fnives.android.test.dockerfile.util.assertCollectionsEquals
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.io.File
|
||||
|
||||
internal class DockerFileWriterTest {
|
||||
|
||||
@TempDir
|
||||
lateinit var tempDirectory: File
|
||||
|
||||
@Test
|
||||
fun createsFolder() {
|
||||
val folderToCreate = File(tempDirectory, "childFolder")
|
||||
val sut = DockerFileWriter(outputFolder = folderToCreate)
|
||||
|
||||
sut.write(DockerFileContent(name = "alma", content = "-", tag = "123"))
|
||||
|
||||
Assertions.assertTrue(folderToCreate.exists()) { "Temp folder does not exist!" }
|
||||
Assertions.assertFalse(folderToCreate.isFile) { "Temp folder is a file not a Folder!" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun writesFileProperly() {
|
||||
val sut = DockerFileWriter(outputFolder = tempDirectory)
|
||||
val expected = listOf("123", "abc")
|
||||
|
||||
sut.write(DockerFileContent(name = "alma", content = "123\nabc", tag = "123"))
|
||||
val actual = File(tempDirectory, "alma")
|
||||
|
||||
assertCollectionsEquals(expected, actual.readLines())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertReturnsCorrectDescriptor() {
|
||||
val sut = DockerFileWriter(outputFolder = tempDirectory)
|
||||
val expectedFile = File(tempDirectory, "alma")
|
||||
val expected = DockerFileDescriptor(filePath = expectedFile.absolutePath, tag = "123")
|
||||
|
||||
val actual = sut.write(DockerFileContent(name = "alma", content = "123\nabc", tag = "123"))
|
||||
|
||||
Assertions.assertEquals(expected, actual)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package org.fnives.android.test.dockerfile.write
|
||||
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.io.File
|
||||
|
||||
internal class ScriptCopierTest {
|
||||
|
||||
@TempDir
|
||||
lateinit var tempDirectory: File
|
||||
|
||||
@Test
|
||||
fun copyExample() {
|
||||
val sut = ScriptCopier(
|
||||
outputFolder = tempDirectory,
|
||||
scriptName = "example_to_copy"
|
||||
)
|
||||
|
||||
val actual = File(tempDirectory, "example_to_copy")
|
||||
sut.invoke()
|
||||
|
||||
val expected = arrayOf("123", "ab", "456")
|
||||
Assertions.assertArrayEquals(expected, actual.readLines().toTypedArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun checkActualCopyIsNotEmpty() {
|
||||
val sut = ScriptCopier(
|
||||
outputFolder = tempDirectory,
|
||||
scriptName = "startemulator"
|
||||
)
|
||||
|
||||
val actual = File(tempDirectory, "startemulator")
|
||||
sut.invoke()
|
||||
|
||||
Assertions.assertTrue(actual.readLines().isNotEmpty()) { "Content is empty when trying to copy startemulator" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createsFolder() {
|
||||
val folderToCreate = File(tempDirectory, "childFolder")
|
||||
val sut = ScriptCopier(
|
||||
outputFolder = folderToCreate,
|
||||
scriptName = "example_to_copy"
|
||||
)
|
||||
|
||||
sut.invoke()
|
||||
|
||||
Assertions.assertTrue(folderToCreate.exists()) { "Temp folder does not exist!" }
|
||||
Assertions.assertFalse(folderToCreate.isFile) { "Temp folder is a file not a Folder!" }
|
||||
}
|
||||
}
|
||||
11
main/src/test/resources/example_configuration.toml
Normal file
11
main/src/test/resources/example_configuration.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
buildTools = ["33.0.0", "32.0.0", "31.0.0", "30.0.3", "30.0.2"]
|
||||
sdks = [30, 31, 32, 33]
|
||||
androidCommandlineTools = "8512546_latest"
|
||||
gradleVersion = "7.3.3"
|
||||
output = "outputs"
|
||||
[variations]
|
||||
apiLevels = [21, 22, 23]
|
||||
[image]
|
||||
repository = "fknives"
|
||||
namespace = "android-test-img"
|
||||
tagPrefix = "1.0.0"
|
||||
3
main/src/test/resources/example_to_copy
Normal file
3
main/src/test/resources/example_to_copy
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
123
|
||||
ab
|
||||
456
|
||||
8
main/src/test/resources/expected_api_27_dockerfile
Normal file
8
main/src/test/resources/expected_api_27_dockerfile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
FROM fknives/ati:1.0
|
||||
# Generated on 2021-11-15
|
||||
|
||||
ENV EMULATOR_API_LEVEL 27
|
||||
|
||||
RUN /usr/local/bin/androidemulatorstart --buildOnly
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
78
main/src/test/resources/expected_generic_dockerfile
Normal file
78
main/src/test/resources/expected_generic_dockerfile
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
FROM openjdk:11-jdk-slim
|
||||
# Generated on 2021-11-15
|
||||
|
||||
# installing usual required tools
|
||||
# build-essential, ruby and bundler is needed for fastlane
|
||||
RUN apt-get update && apt-get install -y \
|
||||
sudo \
|
||||
wget \
|
||||
curl \
|
||||
unzip \
|
||||
android-sdk \
|
||||
build-essential \
|
||||
ruby-full && \
|
||||
gem install bundler
|
||||
|
||||
# versions
|
||||
# latest command lines version here: https://developer.android.com/studio#command-tools
|
||||
ENV ANDROID_COMMAND_LINES_TOOLS_VERSION "8512546_latest"
|
||||
# gradle version can be found in projects gradle-wrapper.properties
|
||||
ENV GRADLE_VERSION "7.3.3"
|
||||
|
||||
# set homes and paths
|
||||
ENV ANDROID_HOME "/usr/lib/android-sdk"
|
||||
ENV ANDROID_SDK_ROOT $ANDROID_HOME
|
||||
ENV CMDLINE_TOOLS_ROOT "${ANDROID_HOME}/cmdline-tools/latest/bin"
|
||||
ENV AVD_HOME "/root/.android/avd"
|
||||
ENV PATH "$ANDROID_HOME/cmdline-tools/latest/bin:${PATH}"
|
||||
ENV PATH "$ANDROID_HOME/emulator:${PATH}"
|
||||
ENV PATH "/usr/local/gradle-${GRADLE_VERSION}/bin:${PATH}"
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
# install gradle
|
||||
RUN curl -sSL -o /tmp/gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip && \
|
||||
unzip -d /usr/local /tmp/gradle.zip && \
|
||||
rm -rf /tmp/gradle.zip && \
|
||||
echo "1" | gradle init && \
|
||||
/root/gradlew && \
|
||||
rm -rf /root/* && \
|
||||
rm /root/.gitignore
|
||||
|
||||
# install command line tools
|
||||
RUN wget https://dl.google.com/android/repository/commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
unzip commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip -d cmdline-tools && \
|
||||
rm commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
mv cmdline-tools/cmdline-tools/ cmdline-tools/latest && \
|
||||
mv cmdline-tools $ANDROID_HOME/
|
||||
|
||||
# install emulator and setup sdkmanager
|
||||
RUN yes | sdkmanager --licenses > /dev/null && \
|
||||
sdkmanager --install emulator --channel=0 > /dev/null
|
||||
|
||||
# support libraries and google play services
|
||||
RUN echo y | sdkmanager "extras;android;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;google_play_services"
|
||||
|
||||
# build tools versions and sdk versions can be found via sdkmanager --list and filtering it
|
||||
# install build-tools
|
||||
# example: `echo y | sdkmanager "build-tools;33.0.0"`
|
||||
RUN echo y | sdkmanager "build-tools;30.0.0" && \
|
||||
echo y | sdkmanager "build-tools;32.0.0" && \
|
||||
echo y | sdkmanager "build-tools;31.0.0" && \
|
||||
echo y | sdkmanager "build-tools;30.0.3" && \
|
||||
echo y | sdkmanager "build-tools;30.0.2"
|
||||
|
||||
# install sdks
|
||||
# example: `echo y | sdkmanager "platforms;android-33"`
|
||||
RUN echo y | sdkmanager "platforms;android-33" && \
|
||||
echo y | sdkmanager "platforms;android-32" && \
|
||||
echo y | sdkmanager "platforms;android-31" && \
|
||||
echo y | sdkmanager "platforms;android-30"
|
||||
|
||||
# copy script to install sdk, create emulator and boot it
|
||||
COPY ./startemulator /usr/local/bin/androidemulatorstart
|
||||
RUN chmod 744 /usr/local/bin/androidemulatorstart
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
FROM fknives2/android-test-img-multi:1.0.1
|
||||
# Generated on 2016-05-28
|
||||
|
||||
ENV EMULATOR_API_LEVEL 22
|
||||
|
||||
RUN /usr/local/bin/androidemulatorstart --buildOnly
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
FROM fknives2/android-test-img-multi:1.0.1
|
||||
# Generated on 2016-05-28
|
||||
|
||||
ENV EMULATOR_API_LEVEL 30
|
||||
|
||||
RUN /usr/local/bin/androidemulatorstart --buildOnly
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
FROM openjdk:11-jdk-slim
|
||||
# Generated on 2016-05-28
|
||||
|
||||
# installing usual required tools
|
||||
# build-essential, ruby and bundler is needed for fastlane
|
||||
RUN apt-get update && apt-get install -y \
|
||||
sudo \
|
||||
wget \
|
||||
curl \
|
||||
unzip \
|
||||
android-sdk \
|
||||
build-essential \
|
||||
ruby-full && \
|
||||
gem install bundler
|
||||
|
||||
# versions
|
||||
# latest command lines version here: https://developer.android.com/studio#command-tools
|
||||
ENV ANDROID_COMMAND_LINES_TOOLS_VERSION "8512546_latest"
|
||||
# gradle version can be found in projects gradle-wrapper.properties
|
||||
ENV GRADLE_VERSION "7.3.3"
|
||||
|
||||
# set homes and paths
|
||||
ENV ANDROID_HOME "/usr/lib/android-sdk"
|
||||
ENV ANDROID_SDK_ROOT $ANDROID_HOME
|
||||
ENV CMDLINE_TOOLS_ROOT "${ANDROID_HOME}/cmdline-tools/latest/bin"
|
||||
ENV AVD_HOME "/root/.android/avd"
|
||||
ENV PATH "$ANDROID_HOME/cmdline-tools/latest/bin:${PATH}"
|
||||
ENV PATH "$ANDROID_HOME/emulator:${PATH}"
|
||||
ENV PATH "/usr/local/gradle-${GRADLE_VERSION}/bin:${PATH}"
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
# install gradle
|
||||
RUN curl -sSL -o /tmp/gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip && \
|
||||
unzip -d /usr/local /tmp/gradle.zip && \
|
||||
rm -rf /tmp/gradle.zip && \
|
||||
echo "1" | gradle init && \
|
||||
/root/gradlew && \
|
||||
rm -rf /root/* && \
|
||||
rm /root/.gitignore
|
||||
|
||||
# install command line tools
|
||||
RUN wget https://dl.google.com/android/repository/commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
unzip commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip -d cmdline-tools && \
|
||||
rm commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
mv cmdline-tools/cmdline-tools/ cmdline-tools/latest && \
|
||||
mv cmdline-tools $ANDROID_HOME/
|
||||
|
||||
# install emulator and setup sdkmanager
|
||||
RUN yes | sdkmanager --licenses > /dev/null && \
|
||||
sdkmanager --install emulator --channel=0 > /dev/null
|
||||
|
||||
# support libraries and google play services
|
||||
RUN echo y | sdkmanager "extras;android;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;google_play_services"
|
||||
|
||||
# build tools versions and sdk versions can be found via sdkmanager --list and filtering it
|
||||
# install build-tools
|
||||
# example: `echo y | sdkmanager "build-tools;33.0.0"`
|
||||
RUN echo y | sdkmanager "build-tools;33.0.0" && \
|
||||
echo y | sdkmanager "build-tools;32.0.0"
|
||||
|
||||
# install sdks
|
||||
# example: `echo y | sdkmanager "platforms;android-33"`
|
||||
RUN echo y | sdkmanager "platforms;android-30" && \
|
||||
echo y | sdkmanager "platforms;android-31"
|
||||
|
||||
# copy script to install sdk, create emulator and boot it
|
||||
COPY ./startemulator /usr/local/bin/androidemulatorstart
|
||||
RUN chmod 744 /usr/local/bin/androidemulatorstart
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
FROM openjdk:11-jdk-slim
|
||||
# Generated on 2016-05-28
|
||||
|
||||
# installing usual required tools
|
||||
# build-essential, ruby and bundler is needed for fastlane
|
||||
RUN apt-get update && apt-get install -y \
|
||||
sudo \
|
||||
wget \
|
||||
curl \
|
||||
unzip \
|
||||
android-sdk \
|
||||
build-essential \
|
||||
ruby-full && \
|
||||
gem install bundler
|
||||
|
||||
# versions
|
||||
# latest command lines version here: https://developer.android.com/studio#command-tools
|
||||
ENV ANDROID_COMMAND_LINES_TOOLS_VERSION "8512546_latest"
|
||||
# gradle version can be found in projects gradle-wrapper.properties
|
||||
ENV GRADLE_VERSION "7.3.3"
|
||||
|
||||
# set homes and paths
|
||||
ENV ANDROID_HOME "/usr/lib/android-sdk"
|
||||
ENV ANDROID_SDK_ROOT $ANDROID_HOME
|
||||
ENV CMDLINE_TOOLS_ROOT "${ANDROID_HOME}/cmdline-tools/latest/bin"
|
||||
ENV AVD_HOME "/root/.android/avd"
|
||||
ENV PATH "$ANDROID_HOME/cmdline-tools/latest/bin:${PATH}"
|
||||
ENV PATH "$ANDROID_HOME/emulator:${PATH}"
|
||||
ENV PATH "/usr/local/gradle-${GRADLE_VERSION}/bin:${PATH}"
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
# install gradle
|
||||
RUN curl -sSL -o /tmp/gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip && \
|
||||
unzip -d /usr/local /tmp/gradle.zip && \
|
||||
rm -rf /tmp/gradle.zip && \
|
||||
echo "1" | gradle init && \
|
||||
/root/gradlew && \
|
||||
rm -rf /root/* && \
|
||||
rm /root/.gitignore
|
||||
|
||||
# install command line tools
|
||||
RUN wget https://dl.google.com/android/repository/commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
unzip commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip -d cmdline-tools && \
|
||||
rm commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
mv cmdline-tools/cmdline-tools/ cmdline-tools/latest && \
|
||||
mv cmdline-tools $ANDROID_HOME/
|
||||
|
||||
# install emulator and setup sdkmanager
|
||||
RUN yes | sdkmanager --licenses > /dev/null && \
|
||||
sdkmanager --install emulator --channel=0 > /dev/null
|
||||
|
||||
# support libraries and google play services
|
||||
RUN echo y | sdkmanager "extras;android;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;google_play_services"
|
||||
|
||||
# build tools versions and sdk versions can be found via sdkmanager --list and filtering it
|
||||
# install build-tools
|
||||
# example: `echo y | sdkmanager "build-tools;33.0.0"`
|
||||
RUN echo y | sdkmanager "build-tools;33.0.0" && \
|
||||
echo y | sdkmanager "build-tools;32.0.0" && \
|
||||
echo y | sdkmanager "build-tools;31.0.0"
|
||||
|
||||
# install sdks
|
||||
# example: `echo y | sdkmanager "platforms;android-33"`
|
||||
RUN echo y | sdkmanager "platforms;android-32" && \
|
||||
echo y | sdkmanager "platforms;android-33"
|
||||
|
||||
# copy script to install sdk, create emulator and boot it
|
||||
COPY ./startemulator /usr/local/bin/androidemulatorstart
|
||||
RUN chmod 744 /usr/local/bin/androidemulatorstart
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
11
main/src/test/resources/multi_api_configuration.toml
Normal file
11
main/src/test/resources/multi_api_configuration.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
buildTools = ["33.0.0", "32.0.0"]
|
||||
sdks = [30, 31]
|
||||
androidCommandlineTools = "8512546_latest"
|
||||
gradleVersion = "7.3.3"
|
||||
output = "outputs"
|
||||
[variations]
|
||||
apiLevels = [22, 30]
|
||||
[image]
|
||||
repository = "fknives2"
|
||||
namespace = "android-test-img-multi"
|
||||
tagPrefix = "1.0.1"
|
||||
9
main/src/test/resources/single_configuration.toml
Normal file
9
main/src/test/resources/single_configuration.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
buildTools = ["33.0.0", "32.0.0", "31.0.0"]
|
||||
sdks = [32, 33]
|
||||
androidCommandlineTools = "8512546_latest"
|
||||
gradleVersion = "7.3.3"
|
||||
output = "outputs"
|
||||
[image]
|
||||
repository = "fknives"
|
||||
namespace = "android-test-img"
|
||||
tagPrefix = "1.0.0"
|
||||
78
outputs-1.0.0/Dockerfile
Normal file
78
outputs-1.0.0/Dockerfile
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
FROM openjdk:11-jdk-slim
|
||||
# Generated on 2022-08-26
|
||||
|
||||
# installing usual required tools
|
||||
# build-essential, ruby and bundler is needed for fastlane
|
||||
RUN apt-get update && apt-get install -y \
|
||||
sudo \
|
||||
wget \
|
||||
curl \
|
||||
unzip \
|
||||
android-sdk \
|
||||
build-essential \
|
||||
ruby-full && \
|
||||
gem install bundler
|
||||
|
||||
# versions
|
||||
# latest command lines version here: https://developer.android.com/studio#command-tools
|
||||
ENV ANDROID_COMMAND_LINES_TOOLS_VERSION "8512546_latest"
|
||||
# gradle version can be found in projects gradle-wrapper.properties
|
||||
ENV GRADLE_VERSION "7.3.3"
|
||||
|
||||
# set homes and paths
|
||||
ENV ANDROID_HOME "/usr/lib/android-sdk"
|
||||
ENV ANDROID_SDK_ROOT $ANDROID_HOME
|
||||
ENV CMDLINE_TOOLS_ROOT "${ANDROID_HOME}/cmdline-tools/latest/bin"
|
||||
ENV AVD_HOME "/root/.android/avd"
|
||||
ENV PATH "$ANDROID_HOME/cmdline-tools/latest/bin:${PATH}"
|
||||
ENV PATH "$ANDROID_HOME/emulator:${PATH}"
|
||||
ENV PATH "/usr/local/gradle-${GRADLE_VERSION}/bin:${PATH}"
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
# install gradle
|
||||
RUN curl -sSL -o /tmp/gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip && \
|
||||
unzip -d /usr/local /tmp/gradle.zip && \
|
||||
rm -rf /tmp/gradle.zip && \
|
||||
echo "1" | gradle init && \
|
||||
/root/gradlew && \
|
||||
rm -rf /root/* && \
|
||||
rm /root/.gitignore
|
||||
|
||||
# install command line tools
|
||||
RUN wget https://dl.google.com/android/repository/commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
unzip commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip -d cmdline-tools && \
|
||||
rm commandlinetools-linux-$ANDROID_COMMAND_LINES_TOOLS_VERSION.zip && \
|
||||
mv cmdline-tools/cmdline-tools/ cmdline-tools/latest && \
|
||||
mv cmdline-tools $ANDROID_HOME/
|
||||
|
||||
# install emulator and setup sdkmanager
|
||||
RUN yes | sdkmanager --licenses > /dev/null && \
|
||||
sdkmanager --install emulator --channel=0 > /dev/null
|
||||
|
||||
# support libraries and google play services
|
||||
RUN echo y | sdkmanager "extras;android;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;m2repository" && \
|
||||
echo y | sdkmanager "extras;google;google_play_services"
|
||||
|
||||
# build tools versions and sdk versions can be found via sdkmanager --list and filtering it
|
||||
# install build-tools
|
||||
# example: `echo y | sdkmanager "build-tools;33.0.0"`
|
||||
RUN echo y | sdkmanager "build-tools;33.0.0" && \
|
||||
echo y | sdkmanager "build-tools;32.0.0" && \
|
||||
echo y | sdkmanager "build-tools;31.0.0" && \
|
||||
echo y | sdkmanager "build-tools;30.0.3" && \
|
||||
echo y | sdkmanager "build-tools;30.0.2"
|
||||
|
||||
# install sdks
|
||||
# example: `echo y | sdkmanager "platforms;android-33"`
|
||||
RUN echo y | sdkmanager "platforms;android-30" && \
|
||||
echo y | sdkmanager "platforms;android-31" && \
|
||||
echo y | sdkmanager "platforms;android-32" && \
|
||||
echo y | sdkmanager "platforms;android-33"
|
||||
|
||||
# copy script to install sdk, create emulator and boot it
|
||||
COPY ./startemulator /usr/local/bin/androidemulatorstart
|
||||
RUN chmod 744 /usr/local/bin/androidemulatorstart
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
105
outputs-1.0.0/startemulator
Normal file
105
outputs-1.0.0/startemulator
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ "$1" == "--help" ]]
|
||||
then
|
||||
echo "Creates default emulator and starts it."
|
||||
echo "API level and Target can be customized, for more customization use the avdmanager directly!"
|
||||
echo "API_level is read from EMULATOR_API_LEVEL variable"
|
||||
echo "target is read from EMULATOR_TARGET variable or defaults to target"
|
||||
echo "--buildOnly parameter means the emulator will be created, but not started"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
if [[ -z $EMULATOR_API_LEVEL ]]
|
||||
then
|
||||
echo "EMULATOR_API_LEVEL not set!" >&2
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if [[ -z $EMULATOR_TARGET ]]
|
||||
then
|
||||
if [[ $EMULATOR_API_LEVEL -ge 32 ]]
|
||||
then
|
||||
echo "EMULATOR_TARGET not set using \"google_apis\", \"default\" doesn't exists above 32"
|
||||
EMULATOR_TARGET="google_apis"
|
||||
else
|
||||
echo "EMULATOR_TARGET not set using \"default\"" >&2
|
||||
EMULATOR_TARGET="default"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z $AVD_HOME ]]
|
||||
then
|
||||
AVD_HOME="$HOME/.android/avd"
|
||||
echo "AVD_HOME not set, using fallback: $AVD_HOME"
|
||||
fi
|
||||
|
||||
if [[ "$1" == "--buildOnly" ]]
|
||||
then
|
||||
echo "INFO: --buildOnly found, emulator will be created but not started!" >&2
|
||||
fi
|
||||
|
||||
echo "INFO: installing sdk" >&2
|
||||
sdkmanager --install "system-images;android-$EMULATOR_API_LEVEL;$EMULATOR_TARGET;x86_64" --channel=0 > /dev/null 2> /dev/null
|
||||
|
||||
|
||||
echo "INFO: creating emulator" >&2
|
||||
EMULATOR_NAME="test_$EMULATOR_API_LEVEL"
|
||||
echo "no" | avdmanager create avd -n "$EMULATOR_NAME" --abi "$EMULATOR_TARGET/x86_64" --package "system-images;android-$EMULATOR_API_LEVEL;$EMULATOR_TARGET;x86_64"
|
||||
|
||||
echo "INFO: config of emulator:" >&2
|
||||
cat $AVD_HOME/$EMULATOR_NAME.avd/config.ini >&2
|
||||
echo "INFO: modifying config of emulator..." >&2
|
||||
echo 'disk.dataPartition.size=2G\n' >> $AVD_HOME/$EMULATOR_NAME.avd/config.ini
|
||||
|
||||
if [[ "$1" == "--buildOnly" ]]
|
||||
then
|
||||
echo "INFO: emulator created!" >&2
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
echo "INFO: checking devices, expecting nothing..." >&2
|
||||
adb devices
|
||||
|
||||
echo "INFO: starting emulator..." >&2
|
||||
emulator -avd $EMULATOR_NAME -no-snapshot-save -no-window -gpu off -noaudio -no-boot-anim -camera-back none > emulator.log &
|
||||
|
||||
echo "INFO: waiting for emulator..." >&2
|
||||
sleep 30s
|
||||
|
||||
echo "INFO: emulator logs" >&2
|
||||
cat emulator.log
|
||||
|
||||
echo "INFO: checking devices..." >&2
|
||||
adb devices
|
||||
echo "INFO: waiting for emulator to be booted..." >&2
|
||||
BOOT_WAIT_LOG_MESSAGE="INFO: still waiting for emulator to boot"
|
||||
SECONDS_PASSED=0;
|
||||
BOOT_COMPLETE=`adb shell getprop sys.boot_completed`
|
||||
while [[ -z $BOOT_COMPLETE ]];
|
||||
do
|
||||
sleep $BOOT_DELAY_IN_SECONDS;
|
||||
SECONDS_PASSED=$(( $SECONDS_PASSED+$BOOT_DELAY_IN_SECONDS ));
|
||||
echo "INFO: still waiting for emulator to boot ($SECONDS_PASSED seconds passed)"; >&2
|
||||
BOOT_COMPLETE=`adb shell getprop sys.boot_completed`
|
||||
done;
|
||||
echo "INFO: emulator booted." >&2
|
||||
# alternative: https://raw.githubusercontent.com/travis-ci/travis-cookbooks/0f497eb71291b52a703143c5cd63a217c8766dc9/community-cookbooks/android-sdk/files/default/android-wait-for-emulator
|
||||
|
||||
echo "INFO: clearing animations..." >&2
|
||||
adb shell settings put global window_animation_scale 0.0
|
||||
adb shell settings put global transition_animation_scale 0.0
|
||||
adb shell settings put global animator_duration_scale 0.0
|
||||
|
||||
echo "INFO: waiting for emulator have sdk property..." >&2
|
||||
SDK_VERSION=`adb shell getprop ro.build.version.sdk`
|
||||
while [[ -z $SDK_VERSION ]];
|
||||
do
|
||||
sleep $BOOT_DELAY_IN_SECONDS;
|
||||
echo "INFO: still waiting for emulator to have SDK property"; >&2
|
||||
SDK_VERSION=`adb shell getprop ro.build.version.sdk`
|
||||
done;
|
||||
|
||||
echo "INFO: unlocking screen..." >&2
|
||||
adb shell input keyevent 82
|
||||
echo "INFO: emulator ready!" >&2
|
||||
19
settings.gradle.kts
Normal file
19
settings.gradle.kts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
}
|
||||
}
|
||||
rootProject.name = "AndroidTestDockerfile"
|
||||
|
||||
include(":main")
|
||||
Loading…
Add table
Add a link
Reference in a new issue