├── .github ├── docker_build_image.sh ├── docker_push_image.sh ├── versions.env └── workflows │ ├── post_merge_deploy.yml │ └── pre_merge_checks.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── android_base ├── Dockerfile ├── README.md └── tools │ ├── cacerts │ └── start_emulator.sh ├── android_bazel ├── Dockerfile └── README.md ├── android_ndk ├── Dockerfile └── README.md ├── android_studio ├── Dockerfile ├── README.md ├── android_studio_ask_screenshot.png ├── docker_as.sh ├── docker_install.sh └── setup_container.sh └── local_build.sh /.github/docker_build_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | docker build ${DOCKER_FILE_PATH} --build-arg IMAGE_VERSION="${IMAGE_VERSION}" --build-arg NDK_VERSION="${NDK_VERSION}" --build-arg BAZELISK_VERSION="${BAZELISK_VERSION}" --compress -t "menny/${IMAGE_NAME}:${IMAGE_VERSION}-raw" 5 | 6 | SQUASH_MSG="" 7 | if [[ "SQUASH" == "${SQUASH_IMAGE}" ]]; then 8 | # Squashing 9 | pip install docker-squash 10 | 11 | echo "** Docker images on machine:" 12 | docker images 13 | echo "******" 14 | 15 | echo "** Docker history for menny/${IMAGE_NAME}:${IMAGE_VERSION}:" 16 | docker history "menny/${IMAGE_NAME}:${IMAGE_VERSION}-raw" 17 | echo "******" 18 | 19 | COMMITS_COUNT="$(docker history menny/${IMAGE_NAME}:${IMAGE_VERSION}-raw | grep 'buildkit.dockerfile' | wc -l)" 20 | IMAGE_SIZE_RAW=$(docker inspect -f "{{ .Size }}" "menny/${IMAGE_NAME}:${IMAGE_VERSION}-raw") 21 | echo "** docker-squash $COMMITS_COUNT layers" 22 | docker-squash -f "$COMMITS_COUNT" -t "menny/${IMAGE_NAME}:${IMAGE_VERSION}" menny/${IMAGE_NAME}:${IMAGE_VERSION}-raw 23 | echo "******" 24 | 25 | echo "** Docker images on machine after squash:" 26 | docker images 27 | echo "******" 28 | SQUASH_MSG=" Layers ${COMMITS_COUNT}. Size before squashing: ${IMAGE_SIZE_RAW}" 29 | else 30 | echo "Skipping squashing" 31 | SQUASH_MSG=" No squash." 32 | docker tag "menny/${IMAGE_NAME}:${IMAGE_VERSION}-raw" "menny/${IMAGE_NAME}:${IMAGE_VERSION}" 33 | fi 34 | 35 | IMAGE_SIZE_RAW=$(docker inspect -f "{{ .Size }}" menny/${IMAGE_NAME}:${IMAGE_VERSION}) 36 | IMAGE_SIZE=$(echo $IMAGE_SIZE_RAW | numfmt --to=si) 37 | PREVIOUS_SIZE="$(docker manifest inspect menny/${IMAGE_NAME}:latest | jq -r '.config.size + ([.layers[].size] | add)')" 38 | SIZE_MESSAGE="Image size for \`menny/${IMAGE_NAME}:${IMAGE_VERSION}\` is ${IMAGE_SIZE} (or $IMAGE_SIZE_RAW bytes).${SQUASH_MSG} Previous (compressed) size was ${PREVIOUS_SIZE}." 39 | 40 | echo "${SIZE_MESSAGE}" 41 | 42 | if [[ -n "$GITHUB_COMMENT_URL" ]]; then 43 | JSON_DATA=$(jq --null-input --arg body "${SIZE_MESSAGE}" '{ "body": $body }') 44 | curl -s -X POST \ 45 | $GITHUB_COMMENT_URL \ 46 | -H "Content-Type: application/json" \ 47 | -H "Authorization: token $GITHUB_TOKEN" \ 48 | --data "${JSON_DATA}" > /dev/null 49 | fi 50 | -------------------------------------------------------------------------------- /.github/docker_push_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | PREVIOUS_SIZE="$(docker manifest inspect menny/${IMAGE_NAME}:latest | jq -r '.config.size + ([.layers[].size] | add)')" 5 | 6 | docker image tag menny/${IMAGE_NAME}:${IMAGE_VERSION} menny/${IMAGE_NAME}:latest 7 | docker image tag menny/${IMAGE_NAME}:${IMAGE_VERSION} ghcr.io/menny/${IMAGE_NAME}:${IMAGE_VERSION} 8 | docker image tag menny/${IMAGE_NAME}:${IMAGE_VERSION} ghcr.io/menny/${IMAGE_NAME}:latest 9 | 10 | docker push menny/${IMAGE_NAME}:${IMAGE_VERSION} 11 | docker push menny/${IMAGE_NAME}:latest 12 | docker push ghcr.io/menny/${IMAGE_NAME}:${IMAGE_VERSION} 13 | docker push ghcr.io/menny/${IMAGE_NAME}:latest 14 | 15 | # removing to ensure we're pull the right size 16 | docker rmi menny/${IMAGE_NAME}:latest 17 | CURRENT_SIZE="$(docker manifest inspect menny/${IMAGE_NAME}:latest | jq -r '.config.size + ([.layers[].size] | add)')" 18 | 19 | SIZES_MSG="Pushed image 'menny/${IMAGE_NAME}:${IMAGE_VERSION}'. Previous size was ${PREVIOUS_SIZE} and current is ${CURRENT_SIZE}." 20 | 21 | echo "$SIZES_MSG" 22 | 23 | if [[ -n "$GITHUB_COMMENT_URL" ]]; then 24 | JSON_DATA=$(jq --null-input --arg body "${SIZE_MESSAGE}" '{ "body": $body }') 25 | curl -s -X POST \ 26 | $GITHUB_COMMENT_URL \ 27 | -H "Content-Type: application/json" \ 28 | -H "Authorization: token $GITHUB_TOKEN" \ 29 | --data "${JSON_DATA}" > /dev/null 30 | fi 31 | -------------------------------------------------------------------------------- /.github/versions.env: -------------------------------------------------------------------------------- 1 | IMAGE_VERSION=1.21.2 2 | NDK_VERSION=27.2.12479018 3 | BAZELISK_VERSION=v1.24.0 4 | -------------------------------------------------------------------------------- /.github/workflows/post_merge_deploy.yml: -------------------------------------------------------------------------------- 1 | name: post_merge_deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | env: 7 | TERM: dumb 8 | jobs: 9 | build_base: 10 | runs-on: ubuntu-24.04 11 | steps: 12 | - uses: actions/checkout@v3.5.0 13 | - name: Log in to Docker Hub 14 | uses: docker/login-action@v2.1.0 15 | with: 16 | username: ${{ secrets.DOCKER_USER }} 17 | password: ${{ secrets.DOCKER_PASS }} 18 | - name: Log in to the Container registry 19 | uses: docker/login-action@v2.1.0 20 | with: 21 | registry: ghcr.io 22 | username: ${{ github.actor }} 23 | password: ${{ secrets.GITHUB_TOKEN }} 24 | - name: Set versions 25 | uses: tw3lveparsecs/github-actions-set-variables@latest 26 | with: 27 | envFilePath: .github/versions.env 28 | - name: base image 29 | env: 30 | IMAGE_NAME: android_base 31 | DOCKER_FILE_PATH: android_base/. 32 | SQUASH_IMAGE: NO 33 | run: | 34 | ./.github/docker_build_image.sh 35 | ./.github/docker_push_image.sh 36 | - name: generic image 37 | env: 38 | IMAGE_NAME: android 39 | DOCKER_FILE_PATH: . 40 | run: | 41 | ./.github/docker_build_image.sh 42 | ./.github/docker_push_image.sh 43 | build_specialized_images: 44 | strategy: 45 | matrix: 46 | type: [android_ndk, android_bazel] 47 | include: 48 | - type: android_ndk 49 | squash: NO 50 | - type: android_bazel 51 | squash: NO 52 | runs-on: ubuntu-24.04 53 | needs: 54 | - build_base 55 | env: 56 | IMAGE_NAME: ${{ matrix.type }} 57 | DOCKER_FILE_PATH: ${{ matrix.type }}/. 58 | SQUASH_IMAGE: ${{ matrix.squash }} 59 | steps: 60 | - uses: actions/checkout@v3.5.0 61 | - name: Log in to Docker Hub 62 | uses: docker/login-action@v2.1.0 63 | with: 64 | username: ${{ secrets.DOCKER_USER }} 65 | password: ${{ secrets.DOCKER_PASS }} 66 | - name: Log in to the Container registry 67 | uses: docker/login-action@v2.1.0 68 | with: 69 | registry: ghcr.io 70 | username: ${{ github.actor }} 71 | password: ${{ secrets.GITHUB_TOKEN }} 72 | - name: Set versions 73 | uses: tw3lveparsecs/github-actions-set-variables@latest 74 | with: 75 | envFilePath: .github/versions.env 76 | - run: ./.github/docker_build_image.sh 77 | - run: ./.github/docker_push_image.sh 78 | green: 79 | runs-on: ubuntu-24.04 80 | needs: 81 | - build_base 82 | - build_specialized_images 83 | steps: 84 | - name: ready 85 | run: echo "DONE" 86 | tag_at_repo: 87 | runs-on: ubuntu-24.04 88 | needs: 89 | - green 90 | steps: 91 | - uses: actions/checkout@v3.5.0 92 | - name: Set versions 93 | uses: tw3lveparsecs/github-actions-set-variables@latest 94 | with: 95 | envFilePath: .github/versions.env 96 | - uses: avakar/tag-and-release@v1 97 | with: 98 | tag_name: ${{ env.IMAGE_VERSION }} 99 | body: > 100 | **Base**: image with JDK17, Android SDK Manager ([Dockerfile](https://github.com/menny/docker_android/blob/${{ env.IMAGE_VERSION }}/android_base/Dockerfile)):
101 | ``` 102 | docker pull ghcr.io/menny/android_base:${{ env.IMAGE_VERSION }} 103 | ```
104 |
105 | **Android**: Base + Build Tools, Platform level 35 ([Dockerfile](https://github.com/menny/docker_android/blob/${{ env.IMAGE_VERSION }}/Dockerfile)):
106 | ``` 107 | docker pull ghcr.io/menny/android:${{ env.IMAGE_VERSION }} 108 | ```
109 |
110 | **NDK**: Android + NDK ${{ env.NDK_VERSION }} ([Dockerfile](https://github.com/menny/docker_android/blob/${{ env.IMAGE_VERSION }}/android_ndk/Dockerfile)):
111 | ``` 112 | docker pull ghcr.io/menny/android_ndk:${{ env.IMAGE_VERSION }} 113 | ```
114 |
115 | **Bazel**: Android + Bazelisk ${{ env.BAZELISK_VERSION }} and Go ([Dockerfile](https://github.com/menny/docker_android/blob/${{ env.IMAGE_VERSION }}/android_bazel/Dockerfile)):
116 | ``` 117 | docker pull ghcr.io/menny/android_bazel:${{ env.IMAGE_VERSION }} 118 | ``` 119 | 120 | 121 | env: 122 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 123 | -------------------------------------------------------------------------------- /.github/workflows/pre_merge_checks.yml: -------------------------------------------------------------------------------- 1 | name: pre_merge_checks 2 | on: 3 | pull_request: 4 | branches: 5 | - '*' 6 | 7 | env: 8 | TERM: dumb 9 | IMAGE_VERSION: 9.99.9 10 | jobs: 11 | sanity: 12 | runs-on: ubuntu-24.04 13 | env: 14 | GITHUB_COMMENT_URL: ${{ github.event.pull_request.comments_url }} 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - uses: actions/checkout@v3.5.0 18 | - name: lint 19 | uses: luke142367/Docker-Lint-Action@v1.1.1 20 | with: 21 | target: Dockerfile android_base/Dockerfile android_bazel/Dockerfile android_ndk/Dockerfile 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | - name: Set versions 25 | uses: tw3lveparsecs/github-actions-set-variables@latest 26 | with: 27 | envFilePath: .github/versions.env 28 | - name: Base Android image 29 | env: 30 | SQUASH_IMAGE: SQUASH 31 | run: IMAGE_NAME="android_base" DOCKER_FILE_PATH="android_base/." ./.github/docker_build_image.sh 32 | - name: Android image with some tools 33 | run: IMAGE_NAME="android" DOCKER_FILE_PATH="." ./.github/docker_build_image.sh 34 | - name: Android image with Bazel tools 35 | run: IMAGE_NAME="android_bazel" DOCKER_FILE_PATH="android_bazel/." ./.github/docker_build_image.sh 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG IMAGE_VERSION 2 | FROM menny/android_base:${IMAGE_VERSION} 3 | 4 | ARG IMAGE_VERSION 5 | 6 | LABEL description="A general use Android docker for CI" 7 | LABEL version="${IMAGE_VERSION}" 8 | LABEL maintainer="menny@evendanan.net" 9 | 10 | # Install sdk elements (list from "sdkmanager --list") 11 | RUN sdkmanager "build-tools;35.0.0" 12 | 13 | RUN sdkmanager "platforms;android-35" 14 | 15 | #accepting licenses 16 | RUN yes | sdkmanager --licenses 17 | 18 | RUN sdkmanager --version 19 | 20 | WORKDIR /opt/workspace -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dockerfile for Android CI 2 | Latest release: ![](https://github.com/menny/docker_android/workflows/post_merge_deploy/badge.svg?event=push&branch=main) [![Latest release](https://img.shields.io/github/release/menny/docker_android.svg)](https://github.com/menny/docker_android/releases)
3 | My general usage Docker images for Android CI.
4 | These images are available in Docker Hub and [ghcr.io](https://github.com/menny?tab=packages&repo_name=docker_android). 5 | 6 | ## Contains: 7 | 8 | * Based on `ubuntu:jammy` 24.04 9 | * with wget, curl, zip, python, pip, git, make, gcc (and other build-essential) 10 | * Corretto JDK17 - https://github.com/corretto/corretto-17/releases 11 | * cmdline tools 16.0 12 | * Platform tools 35.0.0 13 | * Build Tools 35.0.0 14 | * SDK API 35 15 | * Compressed and squashed into one layer (where makes sense). 16 | 17 | 18 | **NOTE:** This image does not contain NDK. You can use [this](https://github.com/menny/docker_android/tree/master/android_ndk) for an image with the latest NDK. Also, check out a _Proof-of-Concept_ [Docker image](https://github.com/menny/docker_android/tree/master/android_studio) that has Android Studio running inside Docker! 19 | 20 | # Other images: 21 | 22 | * An even slimmer [image](android_base/) (without the APIs and Build-Tools). 23 | * Android latest SDK and latest NDK [image](android_ndk/). 24 | * Android latest SDK and latest Bazel [image](android_bazel/). 25 | 26 | ## Accepting licenses 27 | Getting an error when building Android with this Docker image? Something like this: 28 | ``` 29 | FAILURE: Build failed with an exception. 30 | 31 | * What went wrong: 32 | A problem occurred configuring project ':app'. 33 | > You have not accepted the license agreements of the following SDK components: 34 | [Android SDK Platform 25, Android SDK Build-Tools 25.0.1]. 35 | Before building your project, you need to accept the license agreements and complete the installation of the missing components using the Android Studio SDK Manager. 36 | Alternatively, to learn how to transfer the license agreements from one workstation to another, go to http://d.android.com/r/studio-ui/export-licenses.html 37 | ``` 38 | You'll need to create licenses folder with license files under `${ANDROID_HOME}`. Accept the licenses on you local machine 39 | then create the same files inside the Docker image, using your CI script. Something like this: 40 | ``` 41 | echo -e "8933bad161af4178b1185d1a37fbf41ea5269c55\c" > ${ANDROID_HOME}/licenses/android-sdk-license 42 | echo -e "79120722343a6f314e0719f863036c702b0e6b2a\n84831b9409646a918e30573bab4c9c91346d8abd\c" > ${ANDROID_HOME}/licenses/android-sdk-preview-license 43 | echo -e "8403addf88ab4874007e1c1e80a0025bf2550a37\c" > ${ANDROID_HOME}/licenses/intel-android-sysimage-license 44 | ``` 45 | Also, due to a known [bug](https://code.google.com/p/android/issues/detail?id=2123090), you'll need to run `gradle` twice. So: 46 | ``` 47 | ./gradlew dependencies || true 48 | ./gradlew clean 49 | ``` 50 | 51 | ## Common commands 52 | Build image: `docker build -t menny/android:latest .` 53 | 54 | Pull from Docker Hub: `docker pull menny/android:latest` 55 | 56 | To run image (and attach to STDIN/STDOUT): `docker run -i -t menny/android:latest` 57 | 58 | ## General Docker commands: 59 | To stop *all* Docker containers: `docker stop $(docker ps -a -q)` 60 | 61 | To remove *all* Docker containers: `docker rm -f $(docker ps -a -q)` 62 | 63 | To remove *all* Docker images: `docker rmi -f $(docker images -q)` 64 | -------------------------------------------------------------------------------- /android_base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:jammy@sha256:f470988096c4d77efac9740a1b6700823681af518a17fad30111430b95dfbffa 2 | ARG IMAGE_VERSION 3 | 4 | LABEL description="A general use Android docker for CI" 5 | LABEL version="${IMAGE_VERSION}" 6 | LABEL maintainer="menny@evendanan.net" 7 | 8 | # disable interactive functions 9 | ENV DEBIAN_FRONTEND=noninteractive 10 | ENV TERM=dumb 11 | 12 | # Install Deps and build-essential 13 | RUN apt-get update \ 14 | && apt-get install -y --allow-remove-essential --allow-change-held-packages --no-install-recommends \ 15 | locales software-properties-common ca-certificates build-essential zlib1g-dev \ 16 | pkg-config file \ 17 | python3 python3-pip python3-lxml python3-yaml libxml2-utils yamllint \ 18 | wget curl nano rsync zip psmisc rsyslog jq unzip \ 19 | imagemagick librsvg2-bin \ 20 | java-common \ 21 | && add-apt-repository ppa:git-core/ppa -y \ 22 | && apt-get install git -y --allow-remove-essential --allow-change-held-packages --no-install-recommends \ 23 | && apt-get clean \ 24 | && rm -rf /var/lib/apt/lists/* 25 | 26 | RUN touch /var/log/kern.log ; chown syslog:adm /var/log/kern.log 27 | 28 | RUN locale-gen en_US.UTF-8 29 | ENV LANG=en_US.UTF-8 30 | ENV LANGUAGE=en_US:en 31 | ENV LC_ALL=en_US.UTF-8 32 | 33 | RUN mkdir -p /opt 34 | WORKDIR /opt 35 | COPY tools /opt/tools 36 | RUN chmod +x /opt/tools/start_emulator.sh 37 | 38 | # Setup environment 39 | ENV ANDROID_HOME=/opt/android-sdk-linux 40 | ENV PATH=${PATH}:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools 41 | 42 | # Setup Java 43 | RUN curl https://corretto.aws/downloads/resources/17.0.10.8.1/java-17-amazon-corretto-jdk_17.0.10.8-1_amd64.deb -o jdk-corretto.deb \ 44 | && dpkg --install jdk-corretto.deb \ 45 | && rm jdk-corretto.deb 46 | 47 | RUN java -version 48 | 49 | # Install Android SDK 50 | RUN curl https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -o downloaded_sdk.zip \ 51 | && mkdir -p /opt/android-sdk-linux/cmdline-tools \ 52 | && unzip downloaded_sdk.zip -d /opt/android-sdk-linux/cmdline-tools \ 53 | && rm -f downloaded_sdk.zip \ 54 | && mv /opt/android-sdk-linux/cmdline-tools/cmdline-tools /opt/android-sdk-linux/cmdline-tools/latest 55 | 56 | #accepting licenses 57 | RUN yes | sdkmanager --licenses 58 | 59 | # Install sdk elements (list from "sdkmanager --list") 60 | RUN sdkmanager --list 61 | 62 | RUN sdkmanager "cmdline-tools;16.0" 63 | RUN sdkmanager "platform-tools" 64 | 65 | #accepting licenses 66 | RUN yes | sdkmanager --licenses 67 | 68 | RUN sdkmanager --version 69 | 70 | # GO to workspace 71 | RUN mkdir -p /opt/workspace 72 | WORKDIR /opt/workspace 73 | -------------------------------------------------------------------------------- /android_base/README.md: -------------------------------------------------------------------------------- 1 | # Dockerfile for Android CI [![CircleCI](https://circleci.com/gh/menny/docker_android/tree/master.svg?style=svg)](https://circleci.com/gh/menny/docker_android/tree/master) 2 | My general usage (very up-to-date) Docker image for Android CI. Without anything other than the bare SDK. 3 | 4 | ## Contains: 5 | 6 | * Based on `ubuntu:jammy` 22.04 7 | * with wget, curl, zip, python, python3, pip, rsyslog, git, make, gcc (and other build-essential) 8 | * Corretto JDK17 - https://github.com/corretto/corretto-17/releases 9 | * Compressed and squashed into one layer. 10 | 11 | ## Accepting licenses 12 | Getting an error when building Android with this Docker image? Something like this: 13 | ``` 14 | FAILURE: Build failed with an exception. 15 | 16 | * What went wrong: 17 | A problem occurred configuring project ':app'. 18 | > You have not accepted the license agreements of the following SDK components: 19 | [Android SDK Platform 25, Android SDK Build-Tools 25.0.1]. 20 | Before building your project, you need to accept the license agreements and complete the installation of the missing components using the Android Studio SDK Manager. 21 | Alternatively, to learn how to transfer the license agreements from one workstation to another, go to http://d.android.com/r/studio-ui/export-licenses.html 22 | ``` 23 | You'll need to create licenses folder with license files under `${ANDROID_HOME}`. Accept the licenses on you local machine 24 | then create the same files inside the Docker image, using your CI script. Something like this: 25 | ``` 26 | echo -e "8933bad161af4178b1185d1a37fbf41ea5269c55\c" > ${ANDROID_HOME}/licenses/android-sdk-license 27 | echo -e "79120722343a6f314e0719f863036c702b0e6b2a\n84831b9409646a918e30573bab4c9c91346d8abd\c" > ${ANDROID_HOME}/licenses/android-sdk-preview-license 28 | echo -e "8403addf88ab4874007e1c1e80a0025bf2550a37\c" > ${ANDROID_HOME}/licenses/intel-android-sysimage-license 29 | ``` 30 | Also, due to a known [bug](https://code.google.com/p/android/issues/detail?id=2123090), you'll need to run `gradle` twice. So: 31 | ``` 32 | ./gradlew dependencies || true 33 | ./gradlew clean 34 | ``` 35 | 36 | ## Common commands 37 | Build image: `docker build -t menny/android_base:latest .` 38 | 39 | Pull from Docker Hub: `docker pull menny/android_base:latest` 40 | 41 | To run image (and attach to STDIN/STDOUT): `docker run -i -t menny/android_base:latest` 42 | 43 | ## General Docker commands: 44 | To stop *all* Docker containers: `docker stop $(docker ps -a -q)` 45 | 46 | To remove *all* Docker containers: `docker rm -f $(docker ps -a -q)` 47 | 48 | To remove *all* Docker images: `docker rmi -f $(docker images -q)` 49 | -------------------------------------------------------------------------------- /android_base/tools/cacerts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menny/docker_android/704858e5cdeca23b326f4f2eac917d0c58b2f14a/android_base/tools/cacerts -------------------------------------------------------------------------------- /android_base/tools/start_emulator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DEFAULT_API=23 4 | BUILD_TOOLS="23.0.3" 5 | 6 | function print_help_and_exit() { 7 | echo "./start_emulator.sh [options]" 8 | echo "Options:" 9 | echo "--api=[api level] : starts an x86 emulator of a specific API level. Default value is ${DEFAULT_API}." 10 | echo "--tablet : starts an x86 emulator with a Nexus 9 configutation. Default is Nexus 5." 11 | echo "--update : Updates your local SDK. Default is not to update." 12 | echo "--headless : Runs the emulator in a headless (CI) mode. Default is with UI (non-headless)." 13 | exit 0 14 | } 15 | 16 | API=$DEFAULT_API 17 | IS_TABLET=0 18 | UPDATE_SDK=0 19 | HEADLESS=0 20 | 21 | for i in "$@" 22 | do 23 | case $i in 24 | --api=*) 25 | API="${i#*=}" 26 | ;; 27 | --tablet) 28 | IS_TABLET=1 29 | ;; 30 | --help|-h) 31 | print_help_and_exit 32 | ;; 33 | --update_sdk) 34 | UPDATE_SDK=1 35 | ;; 36 | --headless) 37 | HEADLESS=1 38 | ;; 39 | *) 40 | # unknown option 41 | echo "unknown argument ${i}" 42 | print_help_and_exit 43 | ;; 44 | esac 45 | done 46 | 47 | REQUIRED_ADD_ON="addon-google_apis-google-${API}" 48 | REQUIRED_SYS_IMG="sys-img-x86-addon-google_apis-google-${API}" 49 | 50 | TARGET_NAME="Google Inc.:Google APIs:${API}" 51 | TARGET_ID_NUMNER_TEXT=$(android list targets | grep "${TARGET_NAME}" | egrep -o "id: [0-9]+") 52 | if [ -z "${TARGET_ID_NUMNER_TEXT}" ]; then 53 | echo "Could not find System image for API ${API}. Forcing SDK update." 54 | UPDATE_SDK=1 55 | fi 56 | 57 | if [ ${UPDATE_SDK} -eq 1 ]; then 58 | echo "Fetching required SDK packages for emulator with API ${API} and is-tablet=${IS_TABLET}" 59 | 60 | android update sdk --all --no-ui --filter platform-tools,build-tools-${BUILD_TOOLS},extra-intel-Hardware_Accelerated_Execution_Manager,android-${DEFAULT_API},addon-google_apis-google-${DEFAULT_API},${REQUIRED_ADD_ON},extra-android-support,extra-android-m2repository,extra-google-m2repository,extra-google-google_play_services,${REQUIRED_SYS_IMG} 61 | fi 62 | 63 | TARGET_ID_NUMNER_TEXT=$(android list targets | grep "${TARGET_NAME}" | egrep -o "id: [0-9]+") 64 | TARGET_ID_NUMNER=${TARGET_ID_NUMNER_TEXT:4} 65 | 66 | if [ ${IS_TABLET} -eq 1 ]; then 67 | DEV_WIDTH="2048" 68 | DEV_HEIGHT="1536" 69 | DPI="320" 70 | ORIENTATION="landscape" 71 | AVD_NAME="Tablet_${API}" 72 | else 73 | DEV_WIDTH="1080" 74 | DEV_HEIGHT="1900" 75 | ORIENTATION="portrait" 76 | DPI="420" 77 | AVD_NAME="Phone_${API}" 78 | fi 79 | 80 | #checking if we have this emulator already created 81 | if [ -z "$(android list avd -c | grep ${AVD_NAME})" ]; then 82 | echo "Creating emulator with target-id ${TARGET_ID_NUMNER}, API ${API} and is-tablet=${IS_TABLET}" 83 | android create avd --name "${AVD_NAME}" --target ${TARGET_ID_NUMNER} -c 200M -s ${DEV_WIDTH}x${DEV_HEIGHT} --tag google_apis --abi x86 84 | #tweaking 85 | echo "hw.gpu.enabled=yes 86 | hw.gpu.mode=auto 87 | hw.keyboard=yes 88 | hw.lcd.density=420 89 | hw.mainKeys=no 90 | hw.ramSize=1536 91 | hw.sdCard=yes 92 | hw.sensors.orientation=yes 93 | hw.sensors.proximity=yes 94 | hw.lcd.width=${DEV_WIDTH} 95 | hw.lcd.height=${DEV_HEIGHT} 96 | hw.trackBall=no 97 | runtime.network.latency=none 98 | runtime.network.speed=full 99 | runtime.scalefactor=auto 100 | vm.heapSize=64" >> "${HOME}/.android/avd/${AVD_NAME}.avd/config.ini" 101 | fi 102 | 103 | echo "Starting emulator with API ${API} and is-tablet=${IS_TABLET}..." 104 | HEADLESS_ARGS="" 105 | if [ ${HEADLESS} -eq 1 ]; then 106 | echo "Headless mode!" 107 | #-noaudio removed till emulator bug fixed 108 | HEADLESS_ARGS="-no-skin -no-window" 109 | fi 110 | #>/dev/null 2>&1 & 111 | emulator -avd ${AVD_NAME} ${HEADLESS_ARGS} 112 | -------------------------------------------------------------------------------- /android_bazel/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG IMAGE_VERSION 2 | FROM menny/android:${IMAGE_VERSION} 3 | 4 | ARG IMAGE_VERSION 5 | ARG BAZELISK_VERSION 6 | 7 | LABEL description="A general use Android docker for CI with Bazelisk version ${BAZELISK_VERSION}" 8 | LABEL version="${IMAGE_VERSION}-${BAZELISK_VERSION}" 9 | LABEL maintainer="menny@evendanan.net" 10 | 11 | WORKDIR /opt 12 | 13 | # removing unsupported OpenJDK arg 14 | ENV JAVA_TOOL_OPTIONS="" 15 | 16 | # Install Go (required by Bazelisk) 17 | RUN apt update \ 18 | && apt install -y --allow-remove-essential --allow-change-held-packages \ 19 | golang \ 20 | && apt-get clean \ 21 | && rm -rf /var/lib/apt/lists/* 22 | 23 | # Install bazelisk 24 | RUN mkdir /opt/bazelisk 25 | ADD https://github.com/bazelbuild/bazelisk/releases/download/${BAZELISK_VERSION}/bazelisk-linux-amd64 /opt/bazelisk/ 26 | RUN mv /opt/bazelisk/bazelisk-linux-amd64 /opt/bazelisk/bazelisk 27 | RUN chmod +x /opt/bazelisk/bazelisk 28 | RUN /opt/bazelisk/bazelisk version 29 | RUN ln -s /opt/bazelisk/bazelisk /opt/bazelisk/bazel 30 | ENV PATH ${PATH}:/opt/bazelisk 31 | # this will ensure we installed correctly, and will extract the installation 32 | RUN bazelisk version 33 | RUN bazel version 34 | # GO to workspace 35 | WORKDIR /opt/workspace 36 | -------------------------------------------------------------------------------- /android_bazel/README.md: -------------------------------------------------------------------------------- 1 | # Dockerfile for Android CI with Bazel [![CircleCI](https://circleci.com/gh/menny/docker_android/tree/master.svg?style=svg)](https://circleci.com/gh/menny/docker_android/tree/master) 2 | My general usage (very up-to-date) Docker image for Android with latest Bazelisk 3 | 4 | ## Contains: 5 | 6 | * All the good staff from the [General Android image](https://github.com/menny/docker_android/blob/master/README.md) 7 | * Go 1.14 8 | * Bazelisk v1.10.1 9 | 10 | ## Accepting licenses 11 | [Read](https://github.com/menny/docker_android/blob/master/README.md#accepting-licenses) about this at the general Android Docker image. 12 | 13 | ## Common commands 14 | Build image: `docker build -t menny/android_bazel:latest .` 15 | 16 | Pull from Docker Hub: `docker pull menny/android_bazel:latest` -------------------------------------------------------------------------------- /android_ndk/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG IMAGE_VERSION 2 | FROM menny/android:${IMAGE_VERSION} 3 | 4 | ARG IMAGE_VERSION 5 | ARG NDK_VERSION 6 | LABEL description="A general use Android docker for CI with NDK ${NDK_VERSION}" 7 | LABEL version="${IMAGE_VERSION}" 8 | LABEL maintainer="menny@evendanan.net" 9 | 10 | WORKDIR /opt 11 | 12 | # Install Android NDK 13 | RUN sdkmanager "ndk;${NDK_VERSION}" 14 | RUN sdkmanager "cmake;3.31.0" 15 | RUN cat /opt/android-sdk-linux/ndk/${NDK_VERSION}/source.properties 16 | 17 | # GO to workspace 18 | WORKDIR /opt/workspace 19 | -------------------------------------------------------------------------------- /android_ndk/README.md: -------------------------------------------------------------------------------- 1 | # Dockerfile for Android CI with NDK [![CircleCI](https://circleci.com/gh/menny/docker_android/tree/master.svg?style=svg)](https://circleci.com/gh/menny/docker_android/tree/master) 2 | My general usage (very up-to-date) Docker image for Android with latest NDK 3 | 4 | ## Contains: 5 | 6 | * All the good staff from the [General Android image](https://github.com/menny/docker_android/blob/master/README.md) 7 | * NDK 23.0.7599858 8 | 9 | ## Accepting licenses 10 | [Read](https://github.com/menny/docker_android/blob/master/README.md#accepting-licenses) about this at the general Android Docker image. 11 | 12 | ## Common commands 13 | Build image: `docker build -t menny/android_ndk:latest .` 14 | 15 | Pull from Docker Hub: `docker pull menny/android_ndk:latest` -------------------------------------------------------------------------------- /android_studio/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM menny/android_ndk:1.8.2 2 | 3 | MAINTAINER Menny Even-Danan "menny@evendanan.net" 4 | LABEL version="1.8.2-3.0.0-RC1" 5 | LABEL description="A Docker image with Android build enviroment and Android Studio" 6 | 7 | WORKDIR /opt 8 | 9 | #ideas taken from https://hub.docker.com/r/dlsniper/docker-intellij/~/dockerfile/ 10 | 11 | # Install Android Studio 12 | RUN wget --output-document=android-studio-linux.zip --quiet https://dl.google.com/dl/android/studio/ide-zips/3.0.0.16/android-studio-ide-171.4392136-linux.zip && \ 13 | unzip android-studio-linux.zip && \ 14 | rm -f android-studio-linux.zip 15 | 16 | #adding adding sources 17 | RUN sdkmanager "sources;android-26" 18 | 19 | # support for X-server 20 | RUN sed 's/main$/main universe/' -i /etc/apt/sources.list && \ 21 | apt-get update -qq && \ 22 | apt-get install -qq -y --fix-missing software-properties-common git libxext-dev libxrender-dev libxslt1.1 \ 23 | libxtst-dev libgtk2.0-0 libcanberra-gtk-module unzip wget && \ 24 | apt-get clean -qq -y && \ 25 | apt-get autoclean -qq -y && \ 26 | apt-get autoremove -qq -y && \ 27 | rm -rf /tmp/* 28 | 29 | # Links2 web-browser. 30 | RUN apt-get install -qq -y --fix-missing links2 && \ 31 | apt-get clean -qq -y && \ 32 | apt-get autoclean -qq -y && \ 33 | apt-get autoremove -qq -y && \ 34 | rm -rf /tmp/* 35 | -------------------------------------------------------------------------------- /android_studio/README.md: -------------------------------------------------------------------------------- 1 | # Dockerfile for Android development with NDK and Android Studio 2 | Docker image for desktop development using Android Studio, based on menny/android_ndk image.
3 | 4 | Like this (artifacts only in the screenshot, not in real life): 5 | 6 | ![Screenshot of AnySoftKeyboard inside Docker](android_studio_ask_screenshot.png "Screenshot of AnySoftKeyboard inside Docker") 7 | 8 | ## Contains: 9 | 10 | * All the good staff from the [General Android image](https://github.com/menny/docker_android/blob/master/README.md). 11 | * All the good staff from the [NDK Android image](https://github.com/menny/docker_android/blob/master/android_ndk/README.md). 12 | * Android Studio 3.0.0 RC1. 13 | * API 26 Sources. 14 | * X11 support. 15 | * [Links2](http://links.twibright.com/) - A very basic web-browser. 16 | 17 | ## Accepting licenses 18 | [Read](https://github.com/menny/docker_android/blob/master/README.md#accepting-licenses) about this at the general Android Docker image. 19 | 20 | ## Common Docker commands 21 | Build image: `docker build -t menny/android_studio:latest .` 22 | 23 | Pull from Docker Hub: `docker pull menny/android_studio:latest` 24 | 25 | ## How To Use 26 | To work with the Android Studio inside this Docker image, you'll need to run it and direct its X11 connection to the local (host) machine X-Server. 27 | 28 | _Note:_ The following instructions come from [here](https://fredrikaverpil.github.io/2016/07/31/docker-for-mac-and-gui-applications/) and [here](https://hub.docker.com/r/dlsniper/docker-intellij/). 29 | 30 | I assume you have an X-Server, and it accepts network connections. 31 | 32 | ### Running on Mac 33 | If you have a Mac, you will need to install `xquartz`. Run: 34 | ``` 35 | brew cask install xquartz 36 | ``` 37 | Start `xquartz`: 38 | ``` 39 | open -a XQuartz 40 | ``` 41 | And allow network connections in the _Preferences_ window. More details [here](https://fredrikaverpil.github.io/2016/07/31/docker-for-mac-and-gui-applications/). 42 | Add your machine's local IP to `xhost`: 43 | ``` 44 | ip=$(ifconfig en0 | grep inet | awk '$1=="inet" {print $2}') 45 | /opt/X11/bin/xhost + $ip 46 | ``` 47 | And run Android Studio: 48 | ``` 49 | docker run -d -e DISPLAY=$ip:0 -v /tmp/.X11-unix:/tmp/.X11-unix menny/android_studio:1.8.1 /opt/android-studio/bin/studio.sh 50 | ``` 51 | 52 | ### Running on Linux -- did not verify 53 | Probably just: 54 | ``` 55 | docker run -d -e DISPLAY=$ip:0 -v /tmp/.X11-unix:/tmp/.X11-unix menny/android_studio:1.8.1 /opt/android-studio/bin/studio.sh 56 | ``` 57 | 58 | ### Running on Windows 59 | No idea. 60 | 61 | ## Pro Tip 62 | The process above will give you a blank installation of Android Studio. It's a nice PoC, but the downside is that every time you start the Docker image, you start in the same blank state.
63 | My work-flow is as follow: 64 | 65 | 1. Start the Docker image 66 | 2. Installation wizard will come up, follow all steps. Let it download what it needs. 67 | 3. At this point, you might want copy your `id_rsa` keys into the Docker image, that's in case you need those keys for cloning your repo. 68 | 69 | ``` 70 | docker exec -it hardcore_meninsky bash 71 | ``` 72 | 73 | This will open a `bash` terminal inside the running Docker image (use `docker ps --all` to find the image name, in my example the name was `hardcore_meninsky`). Run these commands: 74 | 75 | ``` 76 | mkdir -p /root/.ssh 77 | chmod 0700 /root/.ssh 78 | exit 79 | ``` 80 | 81 | Back in the host terminal, copy the ssh keys into the image: 82 | 83 | ``` 84 | docker cp ~/.ssh/id_rsa hardcore_meninsky:/root/.ssh/ 85 | docker cp ~/.ssh/id_rsa.pub hardcore_meninsky:/root/.ssh/ 86 | ``` 87 | 88 | 4. Checkout your repo using the _Import from source control_ option. 89 | 5. Once your repo is loaded, setup Android Studio with everything you need (plugins, settings, code style, etc). 90 | 6. You might also want to download the sources for the Android SDK. Maybe compile the app once to make sure everything is fine. 91 | 7. You might also want to setup the `local.properties` file in your checkout repo. Add the SDK and NDK paths: 92 | 93 | ``` 94 | ndk.dir=/opt/android-ndk-linux 95 | sdk.dir=/opt/android-sdk-linux 96 | ``` 97 | 98 | 8. Quit Android Studio. 99 | 9. In the host machine's terminal run `docker ps --all`. You'll see the Android Studio container (probably the top-most) in exited status. Copy it's name (last column). For example: 100 | 101 | ``` 102 | ➜ docker ps --all 103 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 104 | daffb7bbee3f menny/android_studio:1.8.1 "/opt/android-stud..." 21 minutes ago Exited (0) 11 seconds ago hardcore_meninsky 105 | ``` 106 | 107 | 10. Commit that container into a new tag (let's say _warm_android_studio_): `docker commit hardcore_meninsky warm_android_studio`. This might take a while. 108 | 11. You're done! Next time, you can run your warm image: `docker run -d -e DISPLAY=$ip:0 -v /tmp/.X11-unix:/tmp/.X11-unix warm_android_studio` 109 | 12. Or you can also start the same container again: `docker start hardcore_meninsky`. This will start the container with all the changes you made. 110 | 111 | ### More Pro Tips 112 | 113 | - You can setup your container with `local.properties` values and copying rsa keys by calling `./setup_container.sh [container name] [repo folder]`. 114 | - You can use the execution script `docker_as.sh`. This script will start a new container from `menny/android_studio`, or restart an already created container. -------------------------------------------------------------------------------- /android_studio/android_studio_ask_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menny/docker_android/704858e5cdeca23b326f4f2eac917d0c58b2f14a/android_studio/android_studio_ask_screenshot.png -------------------------------------------------------------------------------- /android_studio/docker_as.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if pgrep -x "Docker" > /dev/null 3 | then 4 | echo "Docker ready." 5 | else 6 | echo "Please start Docker and run this again." 7 | exit 1 8 | fi 9 | 10 | if [ "Darwin" == "`uname`" ] 11 | then 12 | if pgrep -x "Xquartz" > /dev/null 13 | then 14 | echo "Quartz ready." 15 | else 16 | echo "Starting Quartz..." 17 | if [ open -a XQuartz ] 18 | then 19 | echo "Done." 20 | else 21 | echo "Failed to start XQuartz. Install via 'brew cask install xquartz'" 22 | exit 1 23 | fi 24 | fi 25 | else 26 | echo "At this moment, this script only supports macOS." 27 | exit 1 28 | fi 29 | 30 | export ip=$(ifconfig en0 | grep inet | awk '$1=="inet" {print $2}') 31 | /opt/X11/bin/xhost + $ip 32 | 33 | if [ "new" == "$1" ]; then 34 | shift # past action 35 | 36 | BASE_RUN_COMMAND="docker run -d --privileged --network=host -e DISPLAY=$ip:0 -v /tmp/.X11-unix:/tmp/.X11-unix " 37 | IMAGE_NAME="menny/android_studio:1.8.2-3.0.0-RC1" 38 | ADDITIONAL_ARGS="" 39 | 40 | while [[ $# -gt 0 ]] 41 | do 42 | key="$1" 43 | case $key in 44 | -i|--image) 45 | IMAGE_NAME="$2" 46 | shift # past argument 47 | shift # past value 48 | if [ "" == "$IMAGE_NAME" ]; then 49 | echo "Please provide an image name to start, or omit the -i argument to use the default." 50 | exit 1 51 | fi 52 | ;; 53 | -a|--docker_args) 54 | ADDITIONAL_ARGS="$2" 55 | shift # past argument 56 | shift # past value 57 | ;; 58 | *) # unknown option 59 | echo "Uknown option '$key' for action 'new'. Valid options:" 60 | echo "-i|--image [image name]" 61 | echo "-a|--docker_args [additional docker args]" 62 | exit 1 63 | ;; 64 | esac 65 | done 66 | ${BASE_RUN_COMMAND} ${ADDITIONAL_ARGS} ${IMAGE_NAME} /opt/android-studio/bin/studio.sh 67 | exit 0 68 | elif [ "start" == "$1" ]; then 69 | if [ "" == "$2" ]; then 70 | echo "Please provide a container to start. Pick one:" 71 | docker ps --all 72 | exit 1 73 | else 74 | docker start $2 75 | docker exec -e DISPLAY=$ip:0 $2 /opt/android-studio/bin/studio.sh 76 | exit 0 77 | fi 78 | else 79 | echo "Unknown action, or none provided. Possible:" 80 | echo "docker_as.sh new -i|--image [image name] -a|--docker_args [additional docker args]" 81 | echo "docker_as.sh start [container name]" 82 | exit 1 83 | fi 84 | -------------------------------------------------------------------------------- /android_studio/docker_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ "" == "$1" ] 3 | then 4 | echo "Please provide the container name to setup as the first argument." 5 | docker ps 6 | exit 1 7 | fi 8 | 9 | if [ "" == "$2" ] 10 | then 11 | echo "Please provide the path to the clone folder (for example '/root/StudioProjects/AnySoftKeyboard'." 12 | exit 1 13 | fi 14 | 15 | if pgrep -x "Docker" > /dev/null 16 | then 17 | echo "Docker ready." 18 | else 19 | echo "Please start Docker and run this again." 20 | exit 1 21 | fi 22 | 23 | docker exec -it ${1} bash -c "cd ${2} && ./gradlew :app:assembleDebug" 24 | docker cp ${1}:${2}/app/build/outputs/apk/debug/app-debug.apk ${TMPDIR}/app-debug.apk 25 | adb install -r ${TMPDIR}/app-debug.apk 26 | -------------------------------------------------------------------------------- /android_studio/setup_container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ "" == "$1" ] 3 | then 4 | echo "Please provide the container name to setup as the first argument." 5 | docker ps 6 | exit 1 7 | fi 8 | 9 | if [ "" == "$2" ] 10 | then 11 | echo "Please provide the path to the clone folder (for example '/root/StudioProjects/AnySoftKeyboard'." 12 | exit 1 13 | fi 14 | 15 | if pgrep -x "Docker" > /dev/null 16 | then 17 | echo "Docker ready." 18 | else 19 | echo "Please start Docker and run this again." 20 | exit 1 21 | fi 22 | 23 | docker exec -it ${1} mkdir -p /root/.ssh 24 | docker exec -it ${1} chmod 0700 /root/.ssh 25 | docker cp ~/.ssh/id_rsa ${1}:/root/.ssh/ 26 | docker cp ~/.ssh/id_rsa.pub ${1}:/root/.ssh/ 27 | 28 | 29 | sdk.dir=/opt/android-sdk-linux 30 | docker exec -it ${1} ndk.dir=/opt/android-ndk-linux >> ${2}/local.properties 31 | docker exec -it ${1} sdk.dir=/opt/android-sdk-linux >> ${2}/local.properties -------------------------------------------------------------------------------- /local_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | IMAGE_VERSION="$1" 6 | NDK_VERSION="$2" 7 | 8 | if [[ -z "$IMAGE_VERSION" ]]; then 9 | echo "Provide image version as the first argument" 10 | exit 1 11 | fi 12 | if [[ -z "$NDK_VERSION" ]]; then 13 | echo "Provide NDK_VERSION version as the second argument" 14 | exit 1 15 | fi 16 | if [[ -z "$IMAGE_VERSION" ]]; then 17 | echo "Provide image version as the first argument" 18 | exit 1 19 | fi 20 | 21 | function build_image() { 22 | local image_name="$1" 23 | docker build . --build-arg IMAGE_VERSION="${IMAGE_VERSION}" --build-arg NDK_VERSION="${NDK_VERSION}" --compress -t "menny/${image_name}:${IMAGE_VERSION}" 24 | } 25 | 26 | pushd android_base 27 | build_image android_base 28 | popd 29 | 30 | build_image android 31 | 32 | pushd android_ndk 33 | build_image android_ndk 34 | popd 35 | --------------------------------------------------------------------------------