Initial version
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
commit
bf80f5e5ec
4
.dockerignore
Normal file
4
.dockerignore
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!Dockerfile
|
||||
!scripts
|
||||
!license_accepter.sh
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
AndroidSDK
|
||||
*/node_modules
|
||||
*/build
|
||||
repos
|
||||
flutter
|
||||
projects/**
|
||||
+projects/.keep
|
0
AndroidSDK/.keep
Normal file
0
AndroidSDK/.keep
Normal file
127
Dockerfile
Normal file
127
Dockerfile
Normal file
@ -0,0 +1,127 @@
|
||||
# pull base image
|
||||
FROM ubuntu:jammy AS android-sdk
|
||||
|
||||
#
|
||||
# From https://github.com/thyrlian/AndroidSDK/blob/master/android-sdk/Dockerfile
|
||||
#
|
||||
|
||||
# support multiarch: i386 architecture
|
||||
# install Java
|
||||
# install essential tools
|
||||
ARG JDK_VERSION=17
|
||||
RUN dpkg --add-architecture i386 && \
|
||||
apt-get update && \
|
||||
apt-get dist-upgrade -y && \
|
||||
apt-get install -y --no-install-recommends libncurses5:i386 libc6:i386 libstdc++6:i386 lib32gcc-s1 lib32ncurses6 lib32z1 zlib1g:i386 && \
|
||||
apt-get install -y --no-install-recommends openjdk-${JDK_VERSION}-jdk && \
|
||||
apt-get install -y --no-install-recommends git wget unzip && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# download and install Gradle
|
||||
# https://services.gradle.org/distributions/
|
||||
ARG GRADLE_VERSION=8.7
|
||||
ARG GRADLE_DIST=bin
|
||||
RUN cd /opt && \
|
||||
wget -q https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-${GRADLE_DIST}.zip && \
|
||||
unzip gradle*.zip && \
|
||||
ls -d */ | sed 's/\/*$//g' | xargs -I{} mv {} gradle && \
|
||||
rm gradle*.zip
|
||||
|
||||
# download and install Kotlin compiler
|
||||
# https://github.com/JetBrains/kotlin/releases/latest
|
||||
ARG KOTLIN_VERSION=1.9.23
|
||||
RUN cd /opt && \
|
||||
wget -q https://github.com/JetBrains/kotlin/releases/download/v${KOTLIN_VERSION}/kotlin-compiler-${KOTLIN_VERSION}.zip && \
|
||||
unzip *kotlin*.zip && \
|
||||
rm *kotlin*.zip
|
||||
|
||||
# download and install Android SDK
|
||||
# https://developer.android.com/studio#command-line-tools-only
|
||||
ARG ANDROID_SDK_VERSION=11076708
|
||||
ENV ANDROID_HOME /opt/android-sdk
|
||||
RUN mkdir -p ${ANDROID_HOME}/cmdline-tools && \
|
||||
wget -q https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip && \
|
||||
unzip *tools*linux*.zip -d ${ANDROID_HOME}/cmdline-tools && \
|
||||
mv ${ANDROID_HOME}/cmdline-tools/cmdline-tools ${ANDROID_HOME}/cmdline-tools/tools && \
|
||||
rm *tools*linux*.zip
|
||||
|
||||
# set the environment variables
|
||||
ENV JAVA_HOME /usr/lib/jvm/java-${JDK_VERSION}-openjdk-amd64
|
||||
ENV GRADLE_HOME /opt/gradle
|
||||
ENV KOTLIN_HOME /opt/kotlinc
|
||||
ENV PATH ${PATH}:${GRADLE_HOME}/bin:${KOTLIN_HOME}/bin:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/cmdline-tools/tools/bin:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/emulator
|
||||
# WORKAROUND: for issue https://issuetracker.google.com/issues/37137213
|
||||
ENV LD_LIBRARY_PATH ${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib
|
||||
# patch emulator issue: Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
|
||||
# https://doc.qt.io/qt-5/qtwebengine-platform-notes.html#sandboxing-support
|
||||
ENV QTWEBENGINE_DISABLE_SANDBOX 1
|
||||
|
||||
# accept the license agreements of the SDK components
|
||||
ADD license_accepter.sh /opt/
|
||||
RUN chmod +x /opt/license_accepter.sh && /opt/license_accepter.sh $ANDROID_HOME
|
||||
|
||||
FROM android-sdk
|
||||
|
||||
# Seed image with necessary packages
|
||||
RUN apt-get -q update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
git \
|
||||
libglu1-mesa \
|
||||
unzip \
|
||||
wget \
|
||||
xz-utils \
|
||||
zip \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/{apt,dpkg,cache,log}
|
||||
|
||||
# Install Node 18
|
||||
RUN wget -qO- https://deb.nodesource.com/setup_18.x | bash -
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
nodejs \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/{apt,dpkg,cache,log} \
|
||||
&& npm install -g npm@latest
|
||||
|
||||
# install global packages
|
||||
ENV NPM_CONFIG_PREFIX=/home/node/.npm-global
|
||||
ENV PATH /scripts:/home/node/.npm-global/bin:$PATH
|
||||
|
||||
RUN npm i --unsafe-perm --allow-root -g npm@latest
|
||||
|
||||
WORKDIR /projects
|
||||
|
||||
VOLUME "project"
|
||||
|
||||
ENTRYPOINT [ "entrypoint" ]
|
||||
CMD [ "shell" ]
|
||||
|
||||
# set our node environment, either development or production
|
||||
# defaults to production, compose overrides this to development on build and run
|
||||
ARG NODE_ENV=production
|
||||
ENV NODE_ENV $NODE_ENV
|
||||
|
||||
# default to port 19006 for node, and 19001 and 19002 (tests) for debug
|
||||
ARG PORT=19006
|
||||
ENV PORT $PORT
|
||||
EXPOSE $PORT 19001 19002
|
||||
|
||||
# Setup flutter SDK path
|
||||
ENV PATH "/usr/bin/flutter/bin:$PATH"
|
||||
|
||||
# Setup Anrdoid SDK path
|
||||
|
||||
RUN git config --global --add safe.directory /usr/bin/flutter
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
clang \
|
||||
cmake \
|
||||
ninja-build \
|
||||
pkg-config \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/{apt,dpkg,cache,log}
|
||||
|
||||
COPY /scripts /scripts
|
202
LICENSE
Normal file
202
LICENSE
Normal file
@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Files: license_accepter.sh - Copyright 2016-2024 Jing Li from https://github.com/thyrlian/AndroidSDK
|
||||
Files: * - Copyright 2024 James Ketrenos
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
91
README.md
Normal file
91
README.md
Normal file
@ -0,0 +1,91 @@
|
||||
# Android Development Container
|
||||
|
||||
This sets up development containers for Expo and Flutter using
|
||||
the Android SDK. The Dockerfile is derived from parts of [thyrlian/android-sdk](https://github.com/thyrlian/AndroidSDK), with expo and flutter development
|
||||
infrastructure configured as well.
|
||||
|
||||
## Build the Android SDK base container
|
||||
|
||||
This will seed the base container with an initial version of the Android SDK.
|
||||
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
This will build the base 'android-sdk-manager' container as well as the unseeded
|
||||
'android-dev-container'.
|
||||
|
||||
Inside the 'anrdoid-sdk-manager' container the directory '/sdk' will contain the original SDK.
|
||||
|
||||
After it is created, the SDK is copied to a volume bind mount on the host to
|
||||
allow multiple containers to use the same base SDK, as well as to perform
|
||||
SDK updates, upgrades, and additional package installations. The initial
|
||||
seeding of the AndroidSDK on the host is performed via:
|
||||
|
||||
```bash
|
||||
docker compose run -it --rm \
|
||||
android-sdk-manager \
|
||||
init
|
||||
```
|
||||
|
||||
## Install packages into SDK
|
||||
|
||||
The following will install Android platform 35.
|
||||
|
||||
```bash
|
||||
ANDROID_PLATFORM=35
|
||||
docker compose run -it --rm \
|
||||
android-sdk-manager -- \
|
||||
sdkmanager \
|
||||
"build-tools;${ANDROID_PLATFORM}.0.0" \
|
||||
"platforms;android-${ANDROID_PLATFORM}" \
|
||||
"platform-tools"
|
||||
```
|
||||
|
||||
At this point, ./AndroidSDK contains the latest SDK. The docker-compose.yml
|
||||
for the android-dev-container will mount ./AndroidSDK to /opt/android-sdk.
|
||||
|
||||
### Copy the base SDK to the mounted 'sdk' directory
|
||||
|
||||
```bash
|
||||
docker compose run -it --rm \
|
||||
android-dev-container -- \
|
||||
bash -c 'cp -a $ANDROID_HOME/. /sdk/'
|
||||
```
|
||||
|
||||
## Seed the flutter repository
|
||||
|
||||
```
|
||||
git clone https://github.com/flutter/flutter.git
|
||||
```
|
||||
|
||||
|
||||
|
||||
## To seed a new project (once per project)
|
||||
|
||||
```bash
|
||||
PROJECT=foo
|
||||
PROJECT_TYPE=expo # flutter
|
||||
./seed.sh "${PROJECT_TYPE}" "${PROJECT}"
|
||||
```
|
||||
|
||||
## To develop
|
||||
|
||||
```bash
|
||||
PROJECT=foo
|
||||
PROJECT_TYPE=expo
|
||||
./develop.sh "${PROJECT_TYPE}" "${PROJECT}"
|
||||
```
|
||||
|
||||
## To access the shell of the running project
|
||||
|
||||
```bash
|
||||
PROJECT=foo
|
||||
./shell.sh "${PROJECT}"
|
||||
```
|
||||
|
||||
## To stop development container
|
||||
|
||||
```bash
|
||||
docker compose stop "${PROJECT}"
|
||||
```
|
23
develop.sh
Executable file
23
develop.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
declare project=$1
|
||||
|
||||
if [[ "${project}" == "" ]]; then
|
||||
echo "usage: $0 PROJECT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare full_path=$(realpath "projects/${project}")
|
||||
|
||||
if ! docker compose run -it --rm \
|
||||
--name "${project}-seed" \
|
||||
-v "${full_path}:/projects/${project}" \
|
||||
android-dev-container -- \
|
||||
develop \
|
||||
"${project}"; then
|
||||
fail "Unable to launch development container for ${project}"
|
||||
fi
|
40
docker-compose.yml
Normal file
40
docker-compose.yml
Normal file
@ -0,0 +1,40 @@
|
||||
services:
|
||||
android-sdk-manager:
|
||||
build:
|
||||
context: .
|
||||
tty: true
|
||||
ports:
|
||||
- '5037:5037'
|
||||
volumes:
|
||||
- ./AndroidSDK:/sdk:rw
|
||||
# - ./AndroidSDK:/opt/android-sdk:rw
|
||||
- ./scripts:/scripts:rw
|
||||
healthcheck:
|
||||
disable: true
|
||||
|
||||
android-dev-container:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- NODE_ENV=development
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
tty: true
|
||||
ports:
|
||||
- '19006:19006'
|
||||
- '19001:19001'
|
||||
- '19002:19002'
|
||||
volumes:
|
||||
- ./react_native_app:/opt/react_native_app/app:delegated
|
||||
- ./react_native_app/package.json:/opt/react_native_app/package.json
|
||||
- ./react_native_app/package-lock.json:/opt/react_native_app/package-lock.json
|
||||
- ./flutter:/usr/bin/flutter
|
||||
- notused:/opt/react_native_app/app/node_modules
|
||||
- ./repos:/opt/repos
|
||||
- ./AndroidSDK:/opt/android-sdk:ro
|
||||
- ./scripts:/scripts:rw
|
||||
healthcheck:
|
||||
disable: true
|
||||
|
||||
volumes:
|
||||
notused:
|
23
launch.sh
Executable file
23
launch.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
declare project=$1
|
||||
|
||||
if [[ "${project}" == "" ]]; then
|
||||
echo "usage: $0 PROJECT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare full_path=$(realpath "projects/${project}")
|
||||
|
||||
if ! docker compose run --rm \
|
||||
--name "${project}-seed" \
|
||||
-v "${full_path}:/projects/${project}" \
|
||||
android-dev-container -- \
|
||||
run \
|
||||
"${project}"; then
|
||||
fail "Unable to start container for ${project}"
|
||||
fi
|
52
license_accepter.sh
Executable file
52
license_accepter.sh
Executable file
@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
check_android_env_var() {
|
||||
if [ "$#" -lt 1 ]; then
|
||||
if [ -z "${ANDROID_HOME}" ]; then
|
||||
echo "Please either set ANDROID_HOME environment variable, or pass ANDROID_HOME directory as a parameter"
|
||||
exit 1
|
||||
else
|
||||
ANDROID_HOME="${ANDROID_HOME}"
|
||||
fi
|
||||
else
|
||||
ANDROID_HOME=$1
|
||||
fi
|
||||
echo "ANDROID_HOME is at $ANDROID_HOME"
|
||||
}
|
||||
|
||||
accept_all_android_licenses() {
|
||||
ANDROID_LICENSES="$ANDROID_HOME/licenses"
|
||||
if [ ! -d $ANDROID_LICENSES ]; then
|
||||
echo "Android licenses directory doesn't exist, creating one..."
|
||||
mkdir -p $ANDROID_LICENSES
|
||||
fi
|
||||
|
||||
accept_license_of android-googletv-license 601085b94cd77f0b54ff86406957099ebe79c4d6
|
||||
accept_license_of android-sdk-license 8933bad161af4178b1185d1a37fbf41ea5269c55
|
||||
accept_license_of android-sdk-license d56f5187479451eabf01fb78af6dfcb131a6481e
|
||||
accept_license_of android-sdk-license 24333f8a63b6825ea9c5514f83c2829b004d1fee
|
||||
accept_license_of android-sdk-preview-license 84831b9409646a918e30573bab4c9c91346d8abd
|
||||
accept_license_of android-sdk-preview-license 504667f4c0de7af1a06de9f4b1727b84351f2910
|
||||
accept_license_of google-gdk-license 33b6a2b64607f11b759f320ef9dff4ae5c47d97a
|
||||
accept_license_of intel-android-extra-license d975f751698a77b662f1254ddbeed3901e976f5a
|
||||
}
|
||||
|
||||
accept_license_of() {
|
||||
local license=$1
|
||||
local content=$2
|
||||
local file=$ANDROID_LICENSES/$license
|
||||
if [ -f $file ]; then
|
||||
if grep -q "^$content$" $file; then
|
||||
echo "$license: $content has been accepted already"
|
||||
else
|
||||
echo "Accepting $license: $content ..."
|
||||
echo -e $content >> $file
|
||||
fi
|
||||
else
|
||||
echo "Accepting $license: $content ..."
|
||||
echo -e $content > $file
|
||||
fi
|
||||
}
|
||||
|
||||
check_android_env_var "$@"
|
||||
accept_all_android_licenses
|
0
projects/.keep
Normal file
0
projects/.keep
Normal file
0
repos/.keep
Normal file
0
repos/.keep
Normal file
114
scripts/entrypoint
Executable file
114
scripts/entrypoint
Executable file
@ -0,0 +1,114 @@
|
||||
#!/bin/bash
|
||||
script_path=$(dirname "${0}")
|
||||
. "${script_path}/lib/common"
|
||||
|
||||
arguments=(
|
||||
"h|help#This help text."
|
||||
)
|
||||
|
||||
declare -i parse_error=0
|
||||
declare command=""
|
||||
|
||||
#
|
||||
# Parse command line arguments
|
||||
#
|
||||
if ! parse_arguments "${@}"; then
|
||||
parse_error=1
|
||||
fi
|
||||
|
||||
#
|
||||
# Process command line options
|
||||
#
|
||||
eval set -- "${opts}"
|
||||
remaining=$#
|
||||
while (( $# > 0 )); do
|
||||
if [[ ${remaining} == 0 ]]; then
|
||||
fail "case statement is not shifting off values correctly."
|
||||
fi
|
||||
remaining=$((remaining-1))
|
||||
# Uncomment to help debug case statement:
|
||||
# echo "Processing: \$1='$1' \$2='$2'"
|
||||
case "${1}" in
|
||||
-h|--help) # Help / usage
|
||||
usage_extra="[COMMAND [OPTIONS]]"
|
||||
default_usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
declare command
|
||||
declare -a options=()
|
||||
|
||||
if (( ${#} > 0 )); then
|
||||
command="${1}"
|
||||
shift
|
||||
options=("${@}")
|
||||
fi
|
||||
|
||||
if [[ "${command}" == "" ]]; then
|
||||
echo "COMMAND must be supplied." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( parse_error )); then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "${command}" in
|
||||
init)
|
||||
if [[ ! -e "${ANDROID_HOME}"/license/android-sdk-license ]]; then
|
||||
if ! cp -a "${ANDROID_HOME}"/. /sdk; then
|
||||
fail "Unable to copy ${ANDROID_HOME} to /sdk"
|
||||
fi
|
||||
fi
|
||||
echo "AndroidSDK transfered to host bind mount."
|
||||
;;
|
||||
|
||||
seed-project)
|
||||
seed-project "${options[@]}"
|
||||
exit $?
|
||||
;;
|
||||
develop)
|
||||
project="${1}"
|
||||
project_type=$(cat "/projects/${project}/project_type")
|
||||
cd "/projects/${project}"
|
||||
case "${project_type}" in
|
||||
"expo")
|
||||
echo "Launching web app in ${project}"
|
||||
npm run web
|
||||
;;
|
||||
"flutter")
|
||||
echo "Launching terminal in ${project}"
|
||||
/bin/bash
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
run)
|
||||
project="${1}"
|
||||
cd "/projects/${project}"
|
||||
project_type=$(cat "/projects/${project}/project_type")
|
||||
case "${project_type}" in
|
||||
"expo")
|
||||
echo "Starting web app in ${project}"
|
||||
while true; do npm run web; sleep 5; done
|
||||
;;
|
||||
"flutter")
|
||||
echo "Launching terminal in ${project}"
|
||||
/bin/bash
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*bash|shell)
|
||||
/bin/bash "${options[@]}"
|
||||
exit $?
|
||||
;;
|
||||
*)
|
||||
${command} "${@}"
|
||||
;;
|
||||
esac
|
814
scripts/lib/common
Normal file
814
scripts/lib/common
Normal file
@ -0,0 +1,814 @@
|
||||
#!/bin/bash
|
||||
|
||||
# fail ERROR-MESSAGE
|
||||
#
|
||||
# Outputs parameters (ERROR-MESSAGE) passed to 'fail' to stderr
|
||||
#
|
||||
# If a function 'fail_cleanup' exists, execute it after outputting
|
||||
# error message. This allows scripts to perform any necessary script
|
||||
# specific cleanup on failure.
|
||||
#
|
||||
# If 'no_exit' is set, or 'script_name' is 'bash',
|
||||
# do not exit the process and return an error. This lets you chain
|
||||
# error handlers or run the 'irepo' functions from a bash command line
|
||||
# via:
|
||||
# . /opt/scripts/lib/irepo
|
||||
#
|
||||
declare -g fail_failed=0
|
||||
declare -g fail_min_stack=1
|
||||
declare -g fail_skip_continue=0
|
||||
declare -g fail_skip_filepath="/home/user/.${script_name}.skip_continue_target"
|
||||
fail() {
|
||||
local tmp=$?
|
||||
if (( fail_failed )); then
|
||||
exit 1
|
||||
fi
|
||||
fail_failed=${tmp}
|
||||
# On exit 1 from fail, trap is called again. We do not want to display
|
||||
# the stack frame for fail a second time, so set the fail_min_stack
|
||||
# as the limit.
|
||||
if (( ${#} != 0 )); then
|
||||
echo -e "$*" | while read -r line; do
|
||||
echo "FAIL: ${line}" >&2
|
||||
done
|
||||
fail_min_stack=0
|
||||
else
|
||||
echo -e "FAIL: ERR trap triggered: ${script_name} ${full_command}" >&2
|
||||
fail_min_stack=1
|
||||
fi
|
||||
|
||||
if (( fail_skip_continue )); then
|
||||
echo ${BASH_LINENO[0]} > "${fail_skip_filepath}"
|
||||
fi
|
||||
|
||||
# https://bashwizard.com/function-call-stack-and-backtraces/
|
||||
declare CALLSTACK=
|
||||
if (( ${#FUNCNAME[*]} > fail_min_stack )); then
|
||||
CALLSTACK=$(local i ;
|
||||
for (( i=0; i<${#FUNCNAME[*]}; i++ )); do
|
||||
echo "FAIL: ${FUNCNAME[$i]}@${BASH_SOURCE[$i]}:${BASH_LINENO[$i-1]}"
|
||||
done)
|
||||
fi
|
||||
|
||||
echo "${CALLSTACK}" >&2
|
||||
|
||||
if [[ "${FAILURE_TO}" != "" ]]; then
|
||||
echo "Attempting to email ${FAILURE_TO} of failure." >&2
|
||||
declare _dir=$(pwd)
|
||||
_temp=$(mktemp -d)
|
||||
_filename="error-$(date +"%Y%m%d").log"
|
||||
{
|
||||
if [[ -e "${repositories}/.lock" ]]; then
|
||||
echo -e "Lockfile contents:\n"
|
||||
cat "${repositories}/.lock"
|
||||
else
|
||||
echo -e "No lockfile present during failure."
|
||||
fi
|
||||
echo ""
|
||||
echo -e "Callstack:\n"
|
||||
echo "${CALLSTACK}"
|
||||
echo ""
|
||||
echo -e "Environment:\n"
|
||||
export
|
||||
echo ""
|
||||
} >> "${_temp}/${_filename}"
|
||||
cd "${IREPO_HOME}/server"
|
||||
if [[ -e "${log_file}" ]]; then
|
||||
extra_attachments="--files=${log_file}"
|
||||
else
|
||||
extra_attachments=""
|
||||
fi
|
||||
export NODE_EXTRA_CA_CERTS="/usr/local/share/ca-certificates/IntelSHA256RootCA-base64.crt"
|
||||
npm run mailer -- \
|
||||
--to="${FAILURE_TO}" \
|
||||
--from="${FAILURE_FROM:-james.p.ketrenos@intel.com}" \
|
||||
--type="failure" \
|
||||
--files="${_temp}/${_filename}" \
|
||||
"${extra_attachments}"
|
||||
cd "${_dir}"
|
||||
rm -rf "${_temp}"
|
||||
fi
|
||||
|
||||
# If we are in an interactive shell, do not call exit or the shell will
|
||||
# terminate.
|
||||
if [[ "${-}" =~ .*i.* ]]; then
|
||||
kill -INT $$
|
||||
fi
|
||||
|
||||
# execute fail_cleanup if it is declared
|
||||
if [[ $(type -t fail_cleanup) == function ]]; then
|
||||
fail_cleanup
|
||||
fi
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap "fail" ERR
|
||||
|
||||
fail_on_exit() {
|
||||
if (( $# == 0 )); then
|
||||
fail "fail_on_exit must be provided a return code"
|
||||
fi
|
||||
local ret=$1
|
||||
local message=$2
|
||||
message="${message:-unspecified command}"
|
||||
if [[ ${ret} -ne 0 ]]; then
|
||||
fail "${message} failed: ${ret}"
|
||||
return ${ret}
|
||||
fi
|
||||
}
|
||||
|
||||
int() { [ "$1" -eq "$1" ] || fail "$1 is not an integer." ; echo $1 ; }
|
||||
|
||||
declare -g skip_continue_target=0
|
||||
|
||||
# skip_continue
|
||||
#
|
||||
# If called, skip_continue will enable line tracking in the fail ERR trap.
|
||||
# On failure, the file '/home/user/.${script_name}.skip_continue_target'
|
||||
# On exectuion, skip_continue will check the lineno contained in that
|
||||
# file and only return SUCCESS (0) if the current Bash line number
|
||||
# of the caller >= skip_continue_target.
|
||||
#
|
||||
# NOTE: This will not work with nested functions calling skip_continue
|
||||
# from functions not in the main script.
|
||||
skip_continue() {
|
||||
fail_skip_continue=1
|
||||
if (( fail_failed )); then
|
||||
show_vals fail_failed
|
||||
exit ${fail_failed}
|
||||
fi
|
||||
if (( ! skip_continue_target )) && [[ -f "${fail_skip_filepath}" ]]; then
|
||||
skip_continue_target=$(cat "${fail_skip_filepath}")
|
||||
fi
|
||||
|
||||
# Get the line of the skip_continue call
|
||||
local line_no=${BASH_LINENO[0]}
|
||||
line_no=$((line_no+1))
|
||||
|
||||
# Read two lines to see if the skip_continue has the command on
|
||||
# the actual line, or on the following line. Set "line" to that
|
||||
# line for display in the Running or Skipped output
|
||||
mapfile lines < <(head -n ${line_no} "${BASH_SOURCE[1]}"| tail -n 2)
|
||||
if [[ "${lines[0]}" =~ \&\&[[:space:]\\]*\#? ]]; then
|
||||
line="${lines[1]}"
|
||||
else
|
||||
line="${lines[0]%*skip_continue &&}"
|
||||
fi
|
||||
|
||||
if (( line_no >= skip_continue_target )); then
|
||||
echo "Running (${line_no}): ${line%$'\n'}"
|
||||
return 0
|
||||
else
|
||||
echo "Skipping (${line_no}): ${line%$'\n'}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# array_to_csv ARRAY [DELIM]
|
||||
#
|
||||
array_to_csv() {
|
||||
declare -n _arr="${1}"
|
||||
|
||||
local delim="${2}"
|
||||
local joined
|
||||
delim=${delim:-,}
|
||||
printf -v joined "%s${delim}" "${_arr[@]}"
|
||||
joined="${joined%"${delim}"}"
|
||||
joined="${joined#"${delim}"}"
|
||||
echo "${joined}"
|
||||
}
|
||||
|
||||
# csv_to_array ARRAY CSV [DELIM]
|
||||
#
|
||||
csv_to_array() {
|
||||
declare -n _arr="${1}"
|
||||
_arr="${2}"
|
||||
declare _delim="${3:-,}"
|
||||
mapfile -d "${_delim}" -t _arr <<< "${_arr}"
|
||||
count=${#_arr[@]}
|
||||
_arr[count-1]="${_arr[count-1]%$'\n'}"
|
||||
}
|
||||
|
||||
|
||||
# pop TARGET ARRAY
|
||||
#
|
||||
# removes the first element from ARRAY and stores in TARGET
|
||||
pop() {
|
||||
declare -n _target="${1}"
|
||||
declare -n _arr="${2}"
|
||||
if (( ${#_arr[@]} == 0 )); then
|
||||
_target=""
|
||||
return 1
|
||||
fi
|
||||
_target="${_arr[0]}"
|
||||
_arr=("${_arr[@]:1}")
|
||||
return 0
|
||||
}
|
||||
|
||||
# parse_arguments COMMAND-LINE-ARGUMENTS
|
||||
#
|
||||
# Converts 'arguments' variable to long and short parameter definitions
|
||||
#
|
||||
# Parses COMMAND-LINE-ARGUMENTS and sets 'opts' as the parsed parameters
|
||||
# to be used by caller in switch statement.
|
||||
#
|
||||
# NOTE:
|
||||
# Optional parameters must be passed to the command line as
|
||||
# '-param=VALUE' and not as '-param VALUE' or the optional parameter
|
||||
# value will not be parsed and will be moved to unparsed parameters.
|
||||
#
|
||||
# Correct:
|
||||
# app -has-optional=5 -other-param
|
||||
#
|
||||
# Incorrect:
|
||||
# app -has-optional 5 -other-param
|
||||
#
|
||||
# Non-optional parameters can use space, however for consistency it is
|
||||
# recommended that usage help show users to use '=' even for non-optional
|
||||
# parameters.
|
||||
#
|
||||
# See 'arg_example' later in this file for sample usage.
|
||||
#
|
||||
parse_arguments() {
|
||||
local arg_debug=0
|
||||
|
||||
if [[ ${#arguments[@]} == 0 ]]; then
|
||||
fail "No command line arguments options in '\$arguments'"
|
||||
fi
|
||||
local sopts
|
||||
local lopts
|
||||
local arg
|
||||
local _arguments
|
||||
local mandatory=()
|
||||
local valid=()
|
||||
|
||||
for arg in "${arguments[@]}"; do
|
||||
arg="${arg%%\#*}" # Trim off any Help text
|
||||
local -a argument_set
|
||||
csv_to_array argument_set "${arg}" '|' # Convert to array
|
||||
|
||||
for argument in "${argument_set[@]}"; do
|
||||
trimmed=${argument%:} # Trim off 'value required' indicator
|
||||
trimmed=${trimmed%:} # Trim off 'value optional' indicator
|
||||
if [[ "${argument:0:1}" == '*' ]]; then # Check if mandatory
|
||||
argument="${argument:1}" # Strip off '*'
|
||||
trimmed="${trimmed:1}" # Strip off '*'
|
||||
mandatory_arg="${argument_set[*]//:/ }" # Remove :+ from argument_set
|
||||
mandatory_arg="${mandatory_arg//$'*'}" # Remove '*'+ from argument_set
|
||||
mandatory+=("${mandatory_arg}") # Add argument_set to mandatory
|
||||
fi
|
||||
valid+=("${trimmed}")
|
||||
|
||||
if [[ ${#trimmed} == 1 ]]; then
|
||||
sopts="$argument${sopts}"
|
||||
else
|
||||
if [[ "${lopts}" == "" ]]; then
|
||||
lopts=${argument}
|
||||
else
|
||||
lopts=${lopts},${argument}
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
(( arg_debug )) && echo "lopts: ${lopts}" >&2
|
||||
(( arg_debug )) && echo "sopts: ${sopts}" >&2
|
||||
|
||||
# Only accept exact parameters, do not let getopt try and "fit"
|
||||
for opt in "${@}"; do
|
||||
if [[ ! "${opt}" =~ ^- ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ "${opt}" == "--" ]]; then
|
||||
break
|
||||
fi
|
||||
opt=${opt#-}
|
||||
opt=${opt%%=*}
|
||||
if [[ ! " ${valid[*]} " =~ [[:space:]]${opt}[[:space:]] ]]; then
|
||||
echo "ERROR: '-${opt}' is not a valid parameter." >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
opts=$(getopt \
|
||||
-a \
|
||||
--longoptions "${lopts}" \
|
||||
--name "$(basename "$0")" \
|
||||
--options "${sopts}" \
|
||||
-- "$@"
|
||||
)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
(( arg_debug )) && echo "opts: ${opts}"
|
||||
fail "\nSee '$(basename "${0}") -h' for valid options.\n"
|
||||
fi
|
||||
|
||||
# Enforce mandatory options
|
||||
for arg in "${mandatory[@]}"; do
|
||||
provided=0
|
||||
for opt in ${opts}; do
|
||||
if [[ "${opt}" == "--" ]]; then
|
||||
break
|
||||
fi
|
||||
for acceptable in ${arg}; do
|
||||
if [[ "${opt}" =~ ^-\-?"${acceptable}"$ ]]; then
|
||||
provided=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
done
|
||||
if (( ! provided )); then
|
||||
local last_arg=${arg% }
|
||||
last_arg=${last_arg##* }
|
||||
echo "ERROR: '-${last_arg}=' is a mandatory requirement and must be supplied." >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Example function demonstrating argument declaration and parsing.
|
||||
#
|
||||
# You can copy this into your entry script, change the name,
|
||||
# and then call it in the start of your application passing in all
|
||||
# of the command line arguments, for example:
|
||||
#
|
||||
# copy arg_example => process_arguments
|
||||
# process_arguments ${@}
|
||||
#
|
||||
# And then adapt as fit for your application's parameters.
|
||||
#
|
||||
# To run the arg_example script, you can run irepo with
|
||||
# arg_example_show set to a value. For example:
|
||||
#
|
||||
# arg_example_show=1 irepo -h -lp -required-parameter=5
|
||||
#
|
||||
arg_example() {
|
||||
arguments=(
|
||||
"h|help#This help text."
|
||||
"lp" "long-parameter"
|
||||
"rp:" "required-parameter:"
|
||||
"hop::" "has-optional-parameter:"
|
||||
)
|
||||
|
||||
parse_arguments "${@}"
|
||||
eval set -- "$opts"
|
||||
remaining=$#
|
||||
while (( $# > 0 )); do
|
||||
if [[ ${remaining} == 0 ]]; then
|
||||
fail "case statement is not shifting off values correctly."
|
||||
fi
|
||||
remaining=$((remaining-1))
|
||||
# Uncomment to help debug case statement:
|
||||
# echo "Processing: \$1='$1' \$2='$2'"
|
||||
case "${1}" in
|
||||
# NOTE: Short options only have a single dash
|
||||
-h|--help)
|
||||
echo "...show help..."
|
||||
;;
|
||||
# NOTE: Long options always have a double dash, even though the
|
||||
# application reads them from the command-line as a single dash
|
||||
--lp|--long-parameter)
|
||||
echo "...do work for long-parameter..."
|
||||
;;
|
||||
--rp|--required-parameter)
|
||||
value=${2}
|
||||
shift # Remove the argument from the processing list
|
||||
echo "...do work for 'required-parameter=${value}'..."
|
||||
;;
|
||||
--hop|--has-optional-parameter)
|
||||
value=default
|
||||
if [[ "${2}" != "" ]]; then
|
||||
value="${2}"
|
||||
fi
|
||||
shift # Remove the optional argument from the processing list
|
||||
echo "...do work for 'has-optional-parameter=${value}'..."
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
# show_vals VARIABLES
|
||||
#
|
||||
# Helper function for use in debugging. Typical usage:
|
||||
#
|
||||
# show_vals [OPTIONS] VARIABLE1 VARIABLE2 ... VARIABLEn
|
||||
#
|
||||
# Available options: -json -deshadow
|
||||
# -json Outputs as JSON
|
||||
# -deshadow Remove shadow_ prefix from any variable name
|
||||
#
|
||||
show_vals() {
|
||||
declare -i json=0
|
||||
declare -i deshadow=0
|
||||
# Eventually replace this with actual arg parsing, but since
|
||||
# only two options are possible, just check twice to parse both
|
||||
if [[ "${1}" == "-json" ]]; then
|
||||
json=1
|
||||
shift
|
||||
fi
|
||||
if [[ "${1}" == "-deshadow" ]]; then
|
||||
deshadow=1
|
||||
shift
|
||||
fi
|
||||
if [[ "${1}" == "-json" ]]; then
|
||||
json=1
|
||||
shift
|
||||
fi
|
||||
if [[ "${1}" == "-deshadow" ]]; then
|
||||
deshadow=1
|
||||
shift
|
||||
fi
|
||||
local var
|
||||
local value
|
||||
declare -i first=1
|
||||
if (( json )); then
|
||||
echo "{"
|
||||
fi
|
||||
for var in "${@}"; do
|
||||
if (( deshadow )); then
|
||||
var_name="${var/shadow_}"
|
||||
else
|
||||
var_name="${var}"
|
||||
fi
|
||||
if (( first )); then
|
||||
first=0
|
||||
else
|
||||
if ((json )); then
|
||||
echo ","
|
||||
fi
|
||||
fi
|
||||
if [[ "${!var}" == "" ]]; then
|
||||
if (( json )); then
|
||||
echo -n " \"${var_name}\": undefined"
|
||||
else
|
||||
echo "${var_name} = '' or unset"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
value=$(sed -nE "s#declare -a ${var}=\((.*)\)#\1#p" < \
|
||||
<(declare -p "${var}"))
|
||||
if (( json )); then
|
||||
if [[ "${value}" == "" ]]; then
|
||||
echo -n " \"${var_name}\": \"${!var}\""
|
||||
else
|
||||
eval declare -A values=(${value})
|
||||
echo -n " \"${var_name}\": ["
|
||||
declare -i first_arr=1
|
||||
for item in "${values[@]}"; do
|
||||
if (( first_arr )); then
|
||||
first_arr=0
|
||||
else
|
||||
echo -n ", "
|
||||
fi
|
||||
echo -n "\"${item}\""
|
||||
done
|
||||
echo -n "]"
|
||||
fi
|
||||
else
|
||||
if [[ "${value}" == "" ]]; then
|
||||
echo "${var_name} = ${!var}"
|
||||
else
|
||||
echo "${var_name}[] = ${value}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if (( json )); then
|
||||
echo -e "\n}"
|
||||
fi
|
||||
}
|
||||
|
||||
export yes
|
||||
|
||||
# ask VAR OPTS PROMPT
|
||||
#
|
||||
# Will display PROMPT OPTS ? with OPTS split by | and default wrapped
|
||||
# in (). VAR is set to selected option.
|
||||
#
|
||||
# If OPTS contains a capital letter, that is used as the default if
|
||||
# ENTER is pressed.
|
||||
#
|
||||
# Usage:
|
||||
# ask ret yN "Are you sure"
|
||||
# if [[ "${ret}" == "n" ]]; then echo No; fi
|
||||
# if [[ "${ret}" == "y" ]]; then echo Yes; fi
|
||||
#
|
||||
# NOTE: If 'yes' is set in the enviornment, and 'y' is in the option
|
||||
# then 'y' value will be immediately returned after the prompt is shown.
|
||||
#
|
||||
# Will prompt: Are you sure y|(N)?
|
||||
# If neither y, n, or ENTER is pressed, the prompt will loop.
|
||||
# 'res' contains the character the user selected (lowercase)
|
||||
ask() {
|
||||
if [[ "${1}" == "-h" ]] || (( $# < 3 )); then
|
||||
fail "ask RETVAR OPTS PROMPT"
|
||||
fi
|
||||
declare -n _ret="${1}"
|
||||
local opts="${2}a" # Ny - N is default; ynC - C is default; Ynm - Y is default
|
||||
shift 2
|
||||
local prompt="${*}"
|
||||
local index
|
||||
local opts_str
|
||||
local default
|
||||
# Loop through each character in opts and build opt_str
|
||||
# for display to read prompt.
|
||||
while [[ "${opts:${index}:1}" != "" ]]; do
|
||||
local c=${opts:${index}:1}
|
||||
index=$((index+1))
|
||||
if [[ ${c} =~ [A-Z] ]]; then
|
||||
if [[ "${default}" != "" ]]; then
|
||||
fail "Only one character can be default: ${opts} ${default} ${c}"
|
||||
fi
|
||||
if [[ "${opts_str}" != "" ]]; then
|
||||
opts_str="${opts_str}|(${c})"
|
||||
else
|
||||
opts_str="(${c})"
|
||||
fi
|
||||
default=${c,,}
|
||||
else
|
||||
if [[ "${opts_str}" != "" ]]; then
|
||||
opts_str="${opts_str}|${c}"
|
||||
else
|
||||
opts_str="${c}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if (( yes )) && [[ ${opts} =~ [yY] ]]; then
|
||||
# echo "${FUNCNAME[1]}@${BASH_SOURCE[1]}: ${prompt} ${opts_str}? y"
|
||||
echo "${prompt} ${opts_str}? y"
|
||||
_ret="y"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local REPLY
|
||||
while read -n1 -r -p "${prompt} ${opts_str}? " REPLY; do
|
||||
case $REPLY in
|
||||
"a")
|
||||
fail "Abort"
|
||||
;;
|
||||
"")
|
||||
if [[ "${default}" != "" ]]; then
|
||||
_ret="${default,,}"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
if [[ ${REPLY,,} =~ [${opts,,}] ]]; then
|
||||
_ret="${REPLY,,}"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# default_usage
|
||||
#
|
||||
# Display default usage based on ${arguments}
|
||||
#
|
||||
# 'arguments' is an ARRAY of strings.
|
||||
#
|
||||
# Each string is of the form [*]NAME[:](|NAME2[:])#Help text.
|
||||
# If the leading '*' is present, this parameter is mandatory.
|
||||
# If a name is followed by a single ':", that parameter takes a value.
|
||||
# If name is followed by two colons ('::') the parameter is optional.
|
||||
# |NAME2[:] provides the ability to list both short and long parameter
|
||||
# options for a given parameter.
|
||||
# The help text is displayed right aligned.
|
||||
# If a variable NAME or shadow_NAME exists and is set, its value is
|
||||
# displayed in the Help text as the 'Default'.
|
||||
#
|
||||
default_generate_help_text() {
|
||||
for arg in "${@}"; do
|
||||
local _help
|
||||
local _default=
|
||||
local _help_text="${arg#*\#}" # Strip ^.*# from _help_text
|
||||
if [[ "${_help_text}" == "${arg}" ]]; then
|
||||
_help_text=""
|
||||
fi
|
||||
mapfile -t _help_text < <(fold -s -w 50 <<< "${_help_text}")
|
||||
|
||||
if [[ "${arg:0:1}" == '*' ]]; then # Check if is_mandatory
|
||||
arg="${arg:1}" # Strip off '*'
|
||||
is_mandatory=1
|
||||
else
|
||||
is_mandatory=0
|
||||
fi
|
||||
|
||||
arg="${arg%%\#*}" # Trim off any help text
|
||||
local -a argument_set
|
||||
csv_to_array argument_set "${arg}" '|' # Convert to array
|
||||
|
||||
for argument in "${argument_set[@]}"; do
|
||||
if (( ${#argument_set[@]} > 1 )); then
|
||||
argument_value_name=${argument_set[-1]}
|
||||
else
|
||||
argument_value_name=${argument}
|
||||
fi
|
||||
|
||||
if [[ "${argument}" == "${argument_set[0]}" ]]; then
|
||||
first_argument=1
|
||||
else
|
||||
first_argument=0
|
||||
fi
|
||||
if [[ "${argument}" == "${argument_set[-1]}" ]]; then
|
||||
last_argument=1
|
||||
else
|
||||
last_argument=0
|
||||
fi
|
||||
if (( ${#argument_set[@]} > 1 )); then
|
||||
more_than_one_argument=1
|
||||
else
|
||||
more_than_one_argument=0
|
||||
fi
|
||||
|
||||
# If takes option, trim : off end of names
|
||||
if [[ "${argument:0-1}" == ":" ]]; then
|
||||
takes_value=1
|
||||
argument_value_name=${argument_value_name:0:-1}
|
||||
argument="${argument%:}" # Trim off the trailing :
|
||||
else
|
||||
takes_value=0
|
||||
fi
|
||||
|
||||
# If option is optional, trim : off end of names
|
||||
if [[ "${argument:0-1}" == ":" ]]; then
|
||||
takes_value=0
|
||||
takes_optional_value=1
|
||||
argument_value_name=${argument_value_name:0:-1}
|
||||
argument="${argument%:}" # Trim off the trailing :
|
||||
else
|
||||
takes_optional_value=0
|
||||
fi
|
||||
|
||||
if (( is_mandatory )) && (( takes_optional_value )); then
|
||||
fail "'${argument}' Mandatory (*) requirements can not take optional values (::)"
|
||||
fi
|
||||
|
||||
# If this is the first argument, initialize _help from first item
|
||||
# in _help_text. _default="". _default will be set later
|
||||
# if this argument takes parameters.
|
||||
if (( first_argument )); then
|
||||
pop _help _help_text
|
||||
_default=""
|
||||
showed_help=0
|
||||
fi
|
||||
|
||||
if (( takes_value )) || (( takes_optional_value )); then
|
||||
# If a shadow_ variant exists, use that as the default,
|
||||
# otherwise use the argument_value_name (with - changed to _)
|
||||
declare -n _shadow="shadow_${argument_value_name//-/_}"
|
||||
if [[ "${_shadow}" == "" ]]; then
|
||||
declare -n _shadow="${argument_value_name//-/_}"
|
||||
fi
|
||||
# Make the display name for a value for
|
||||
# foo-bar-rabbit into RABBIT
|
||||
argument_value_name=${argument_value_name^^}
|
||||
argument_value_name=${argument_value_name##*-}
|
||||
|
||||
# First argument. Initializes _default.
|
||||
if (( first_argument )); then
|
||||
if (( is_mandatory )); then
|
||||
_default="Mandatory: ${argument_value_name} option must be provied."
|
||||
else
|
||||
if [[ "${_shadow}" != "" ]]; then
|
||||
_default="Default: $(array_to_csv _shadow)"
|
||||
else
|
||||
_default="Default: no default"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# If this is the last argument in the set and the current help
|
||||
# text is empty and there is no more _help_text, set the
|
||||
# _help_text to default
|
||||
if (( last_argument )) &&
|
||||
[[ "${_help}" == "" ]] &&
|
||||
(( ${#_help_text[@]} == 0 )) ; then
|
||||
mapfile -t _help_text < <(fold -s -w 50 <<< "${_default}")
|
||||
_default=""
|
||||
pop _help _help_text
|
||||
fi
|
||||
|
||||
if (( takes_optional_value )); then
|
||||
equals="[=${argument_value_name}]"
|
||||
else
|
||||
equals="=${argument_value_name}"
|
||||
fi
|
||||
|
||||
if (( first_argument )); then
|
||||
# If there is more than one argument in the set, add a | to the
|
||||
# output and do not display _default
|
||||
if (( more_than_one_argument )); then
|
||||
printf " %-30s %s\n" \
|
||||
"-${argument}${equals}|" \
|
||||
"${_help}"
|
||||
showed_help=1
|
||||
else
|
||||
printf " %-30s %s\n" \
|
||||
"-${argument}${equals}" \
|
||||
"${_help}"
|
||||
showed_help=1
|
||||
fi
|
||||
else # (( ! first_argument ))
|
||||
# If not last optional argument, add a | to the output and do
|
||||
# not display _help
|
||||
if (( ! last_argument )); then
|
||||
printf " %-30s %s\n" \
|
||||
"-${argument}${equals}|" \
|
||||
"${_help}"
|
||||
showed_help=1
|
||||
else
|
||||
printf " %-30s %s\n" \
|
||||
"-${argument}${equals}" \
|
||||
"${_help}"
|
||||
showed_help=1
|
||||
fi
|
||||
fi
|
||||
else # (( ! takes_value )) && (( ! takes_optional_value ))
|
||||
if (( first_argument )); then
|
||||
option_string=""
|
||||
# If there is more than one argument in the set, add a | to the
|
||||
# output
|
||||
if (( more_than_one_argument )); then
|
||||
option_string="${option_string}-${argument}|"
|
||||
else # (( last_argument ))
|
||||
printf " %-30s %s\n" "-${argument}" "${_help}"
|
||||
showed_help=1
|
||||
fi
|
||||
else # (( ! first_argument ))
|
||||
# If last non-optional argument, display help
|
||||
if (( last_argument )); then
|
||||
printf " %-30s %s\n" "${option_string}-${argument}" "${_help}"
|
||||
showed_help=1
|
||||
else
|
||||
option_string="${option_string}-${argument}|"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if (( showed_help )); then
|
||||
#show_vals argument argument_set first_argument last_argument more_than_one_argument _help _help_text _default is_mandatory takes_value takes_optional_value showed_help _shadow
|
||||
|
||||
pop _help _help_text
|
||||
fi
|
||||
done # done for 'arg in "${argument_set[@]}"''
|
||||
# If there is still a help string, display it.
|
||||
while [[ "$_help" != "" ]] || (( ${#_help_text[@]} > 0 )); do
|
||||
printf " %-30s %s\n" "" "${_help}"
|
||||
if ! pop _help _help_text; then
|
||||
if [[ "${_default}" == "" ]]; then
|
||||
break
|
||||
fi
|
||||
mapfile -t _help_text < <(fold -s -w 50 <<< "${_default}")
|
||||
_default=""
|
||||
pop _help _help_text
|
||||
fi
|
||||
done
|
||||
_help=""
|
||||
_default=""
|
||||
done
|
||||
}
|
||||
|
||||
default_usage() {
|
||||
local _mandatory=()
|
||||
|
||||
for arg in "${arguments[@]}"; do
|
||||
if [[ "${arg:0:1}" == '*' ]]; then # Check if is_mandatory
|
||||
arg="${arg%#*}" # Strip off help
|
||||
arg="${arg:1:-1}" # Strip off '*'
|
||||
csv_to_array arg "${arg}" '|'
|
||||
_mandatory+=("${arg[-1]}")
|
||||
fi
|
||||
done
|
||||
|
||||
local _text="Usage: ${script_name} [OPTIONS] ${usage_extra}"
|
||||
for _arg in "${_mandatory[@]}"; do
|
||||
_text="${_text} -${_arg}=${_arg^^}"
|
||||
done
|
||||
mapfile -t _text < <(fold -s -w 72 <<< "${_text}")
|
||||
first_line=1
|
||||
for _line in "${_text[@]}"; do
|
||||
if (( first_line )); then
|
||||
echo -e "\n${_line}"
|
||||
first_line=0
|
||||
else
|
||||
echo " ${_line}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "\nOptions:\n"
|
||||
default_generate_help_text "${arguments[@]}"
|
||||
|
||||
echo ""
|
||||
}
|
133
scripts/seed-project
Executable file
133
scripts/seed-project
Executable file
@ -0,0 +1,133 @@
|
||||
#!/bin/bash
|
||||
script_path=$(dirname "${0}")
|
||||
. "${script_path}/lib/common"
|
||||
|
||||
arguments=(
|
||||
"h|help#This help text."
|
||||
"u:|user:#User ID to chown files to"
|
||||
"g:|group:#Group ID to chown files to"
|
||||
)
|
||||
|
||||
declare -i parse_error=0
|
||||
declare project=""
|
||||
declare -i group=1000
|
||||
declare -i user=1000
|
||||
|
||||
#
|
||||
# Parse command line arguments
|
||||
#
|
||||
if ! parse_arguments "${@}"; then
|
||||
parse_error=1
|
||||
fi
|
||||
|
||||
#
|
||||
# Process command line options
|
||||
#
|
||||
eval set -- "${opts}"
|
||||
remaining=$#
|
||||
while (( $# > 0 )); do
|
||||
if [[ ${remaining} == 0 ]]; then
|
||||
fail "case statement is not shifting off values correctly."
|
||||
fi
|
||||
remaining=$((remaining-1))
|
||||
# Uncomment to help debug case statement:
|
||||
# echo "Processing: \$1='$1' \$2='$2'"
|
||||
case "${1}" in
|
||||
-h|--help) # Help / usage
|
||||
usage_extra="[PROJECT]"
|
||||
default_usage
|
||||
exit 0
|
||||
;;
|
||||
-u|--user)
|
||||
user=${1}
|
||||
shift
|
||||
;;
|
||||
-g|--group)
|
||||
group=${1}
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if (( ${#} > 0 )); then
|
||||
project_type=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
if (( ${#} > 0 )); then
|
||||
project=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
if [[ "${project_type}" == "" ]]; then
|
||||
echo "PROJECT-TYPE must be supplied." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${project}" == "" ]]; then
|
||||
echo "PROJECT must be supplied." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( parse_error )); then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "${project_type}" in
|
||||
"expo")
|
||||
if [[ -e "${project}/package.json" ]]; then
|
||||
fail "${project} already exists."
|
||||
fi
|
||||
|
||||
#
|
||||
# 'npx expo init' was replaced with 'npx create-expo-app'
|
||||
#
|
||||
echo "Seeding expo project ${project} in $(pwd)/${project}..."
|
||||
if ! npx create-expo-app "${project}"; then
|
||||
fail "npx create-expo-app ${project}"
|
||||
fi
|
||||
|
||||
cd "${project}"
|
||||
|
||||
if ! npx expo install react-native-web react-dom @expo/webpack-config; then
|
||||
fail "npx expo install react-native-web react-dom @expo/webpack-config"
|
||||
fi
|
||||
|
||||
echo "Installing node packages..."
|
||||
if ! npm install; then
|
||||
fail "npm install"
|
||||
fi
|
||||
|
||||
if ! chown -R ${user}:${group} .; then
|
||||
fail "chown -R ${user}:${group} ."
|
||||
fi
|
||||
|
||||
echo "${project_type}" > project_type
|
||||
;;
|
||||
"flutter")
|
||||
ls -altr /usr/bin/flutter
|
||||
declare version="flutter_linux_3.22.2-stable.tar.xz"
|
||||
if [[ ! -e "/usr/bin/flutter/${version}" ]]; then
|
||||
if ! wget -O "/usr/bin/flutter/${version}" "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/${version}"; then
|
||||
fail "wget ${version}"
|
||||
fi
|
||||
fi
|
||||
if [[ ! -x "/usr/bin/flutter/flutter" ]]; then
|
||||
if ! tar -xf "/usr/bin/flutter/${version}" -C /usr/bin/; then
|
||||
fail "tar ${version}"
|
||||
fi
|
||||
fi
|
||||
git config --global --add safe.directory /usr/bin/flutter
|
||||
cd "${project}"
|
||||
if ! chown -R ${user}:${group} .; then
|
||||
fail "chown -R ${user}:${group} ."
|
||||
fi
|
||||
echo "${project_type}" > project_type
|
||||
;;
|
||||
esac
|
||||
|
35
seed.sh
Executable file
35
seed.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
declare project_type=$1
|
||||
declare project=$2
|
||||
|
||||
if [[ "${project}" == "" ]] || [[ "${project_type}" == "" ]]; then
|
||||
echo "usage: $0 expo|flutter PROJECT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "projects/${project}" ]]; then
|
||||
if ! mkdir -p "projects/${project}"; then
|
||||
fail "mkdir -p projects/${project}"
|
||||
fi
|
||||
fi
|
||||
declare full_path=$(realpath "projects/${project}")
|
||||
|
||||
if ! docker compose run -it --rm \
|
||||
--name "${project}-seed" \
|
||||
-v $(pwd)/scripts:/scripts \
|
||||
-v "${full_path}:/projects/${project}" \
|
||||
android-dev-container -- \
|
||||
seed-project \
|
||||
"${project_type}" \
|
||||
-u $(id -u) \
|
||||
-g $(id) \
|
||||
"${project}"; then
|
||||
fail "Unable to run seed-project for ${project}"
|
||||
else
|
||||
echo "Project '${project}' now exists in projects/${project}"
|
||||
fi
|
21
shell.sh
Executable file
21
shell.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
declare project=$1
|
||||
|
||||
if [[ "${project}" == "" ]]; then
|
||||
echo "usage: ${0} PROJECT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare full_path=$(realpath "projects/${project}")
|
||||
|
||||
if ! docker compose exec -it \
|
||||
"${project}" \
|
||||
shell; then
|
||||
fail "Unable to launch shell in '${project}'. Make sure it is running via './launch.sh ${project}'."
|
||||
fi
|
||||
|
Loading…
x
Reference in New Issue
Block a user