mirror of
https://github.com/expo/turtle.git
synced 2026-11-14 19:12:55 -10:00
Compare commits
83
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0da20f4ab5 | ||
|
|
05d55bf731 | ||
|
|
074ba521f9 | ||
|
|
451999f0b9 | ||
|
|
c0cf273b7c | ||
|
|
cbb8faef12 | ||
|
|
d24b82b5c2 | ||
|
|
3f5ddf6a22 | ||
|
|
80f30bb29c | ||
|
|
c6e2b07fff | ||
|
|
80b0029d57 | ||
|
|
17bda59676 | ||
|
|
2fbcbaa525 | ||
|
|
22e47e4e43 | ||
|
|
1d4728e402 | ||
|
|
de4837f361 | ||
|
|
20d393e48e | ||
|
|
95714bdf5e | ||
|
|
8e2b593e3e | ||
|
|
885cf08a9d | ||
|
|
bd816e1124 | ||
|
|
98549f147c | ||
|
|
9de691c60e | ||
|
|
71e270dc50 | ||
|
|
26e684069d | ||
|
|
1c67df516e | ||
|
|
d7ed489876 | ||
|
|
267e487494 | ||
|
|
b07d830eb9 | ||
|
|
bfaaf09e57 | ||
|
|
33b2a569aa | ||
|
|
2febf6bcdd | ||
|
|
e2c9ee0e39 | ||
|
|
f017540571 | ||
|
|
5b65eb3d6e | ||
|
|
60874f31c9 | ||
|
|
78beb3c0b3 | ||
|
|
03633d94e5 | ||
|
|
392e4487b7 | ||
|
|
f316cb0e0c | ||
|
|
968ce7d8bb | ||
|
|
5876a11568 | ||
|
|
7448e1f4e4 | ||
|
|
4338e71005 | ||
|
|
7323412095 | ||
|
|
c1d5a17e45 | ||
|
|
2771ad0829 | ||
|
|
32cc988072 | ||
|
|
0b3ce2c8e0 | ||
|
|
9c46ee143e | ||
|
|
31c17a4d72 | ||
|
|
ee3f265294 | ||
|
|
c1cca91e68 | ||
|
|
038bafe20b | ||
|
|
f2fef264d7 | ||
|
|
fae77b4455 | ||
|
|
a3ffa0411e | ||
|
|
27dd680cbd | ||
|
|
2b5a370f48 | ||
|
|
21933f5a6a | ||
|
|
a5bd4f0c97 | ||
|
|
8e10aacf59 | ||
|
|
3dd74ef406 | ||
|
|
87f7085fc4 | ||
|
|
0b0b5a7c72 | ||
|
|
d6dee79bcf | ||
|
|
db05eeeed3 | ||
|
|
cc317a6572 | ||
|
|
149073466d | ||
|
|
776aec445b | ||
|
|
d6432b6455 | ||
|
|
81f3a0e300 | ||
|
|
167766519e | ||
|
|
b0d7156691 | ||
|
|
0fdf722a48 | ||
|
|
0382e6bc18 | ||
|
|
07a72ea5cb | ||
|
|
1d3647db98 | ||
|
|
53b96d399f | ||
|
|
78067fc8a6 | ||
|
|
4b29da8f5b | ||
|
|
74d6b37b00 | ||
|
|
d8049d8b83 |
No files matched your search
+196
-14
@@ -1,29 +1,211 @@
|
||||
version: 2
|
||||
version: 2.1
|
||||
|
||||
machine:
|
||||
node:
|
||||
# Circle will use node v4.0.0
|
||||
version: 4.0.0
|
||||
executors:
|
||||
nix: &nix
|
||||
docker:
|
||||
- image: lnl7/nix:2.1.2
|
||||
# Symlink to the readline-enabled bash which is the default command of the container
|
||||
shell: /run/current-system/sw/bin/bash -leo pipefail
|
||||
working_directory: ~/turtle
|
||||
mac:
|
||||
macos: # https://circleci.com/docs/2.0/testing-ios/#supported-xcode-versions
|
||||
xcode: '10.1.0'
|
||||
working_directory: /Users/distiller/project
|
||||
shell: /bin/bash -leo pipefail
|
||||
android:
|
||||
<<: *nix
|
||||
resource_class: xlarge
|
||||
|
||||
commands:
|
||||
install_nix:
|
||||
steps:
|
||||
- run:
|
||||
name: Install nix
|
||||
command: curl https://nixos.org/releases/nix/nix-2.2.1/install | bash -s -- --no-daemon
|
||||
- run:
|
||||
name: Add nix-initializing line to .bashrc
|
||||
command: echo ". ~/.nix-profile/etc/profile.d/nix.sh" >> ~/.bashrc
|
||||
|
||||
setup:
|
||||
steps:
|
||||
- run:
|
||||
name: Install common packages
|
||||
command: |
|
||||
nix-env -iA \
|
||||
nixpkgs.binutils \
|
||||
nixpkgs.direnv \
|
||||
nixpkgs.findutils \
|
||||
nixpkgs.gawk \
|
||||
nixpkgs.git \
|
||||
nixpkgs.gnutar \
|
||||
nixpkgs.gzip \
|
||||
nixpkgs.openssh \
|
||||
nixpkgs.python \
|
||||
nixpkgs.xz
|
||||
- run:
|
||||
name: Configure direnv
|
||||
command: |
|
||||
mkdir -p ~/.config/direnv
|
||||
echo -e "[whitelist]\nprefix = [ \"$HOME\" ]" > ~/.config/direnv/config.toml
|
||||
echo 'eval "$(direnv export bash)"' >> ~/.bash_profile
|
||||
- run:
|
||||
name: Configure yarn
|
||||
command: echo '--frozen-lockfile true' >> ~/.yarnrc
|
||||
- checkout
|
||||
- run:
|
||||
name: Configure aws-cli
|
||||
command: |
|
||||
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
|
||||
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
|
||||
|
||||
setup_nix:
|
||||
steps:
|
||||
- run:
|
||||
name: Add cp and rm stubs
|
||||
command: |
|
||||
cat \<< 'EOF' >> /bin/cp
|
||||
#! /usr/bin/env bash
|
||||
/usr/bin/env cp "$@"
|
||||
EOF
|
||||
chmod +x /bin/cp
|
||||
|
||||
cat \<< 'EOF' >> /bin/rm
|
||||
#! /usr/bin/env bash
|
||||
/usr/bin/env rm "$@"
|
||||
EOF
|
||||
chmod +x /bin/rm
|
||||
|
||||
decrypt_secrets_if_possible:
|
||||
steps:
|
||||
- run:
|
||||
name: Decrypt secrets if possible
|
||||
command: |
|
||||
if [[ -z "$TURTLE_GIT_CRYPT_KEY_BASE64" ]]; then
|
||||
echo 'git-crypt key not present in environment'
|
||||
else
|
||||
git crypt unlock <(echo $TURTLE_GIT_CRYPT_KEY_BASE64 | base64 --decode)
|
||||
fi
|
||||
|
||||
restore_yarn_cache:
|
||||
parameters:
|
||||
workingdir:
|
||||
type: string
|
||||
default: .
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: yarn-v1-{{ checksum "<< parameters.workingdir >>/yarn.lock" }}
|
||||
|
||||
save_yarn_cache:
|
||||
parameters:
|
||||
workingdir:
|
||||
type: string
|
||||
default: .
|
||||
steps:
|
||||
- save_cache:
|
||||
key: yarn-v1-{{ checksum "<< parameters.workingdir >>/yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn # `yarn cache dir`
|
||||
|
||||
restore_workingdir_cache:
|
||||
parameters:
|
||||
platform:
|
||||
type: string
|
||||
default: ios
|
||||
sdk_version:
|
||||
type: string
|
||||
default: sdk32
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: "workingdir-v1-<< parameters.platform >>-<< parameters.sdk_version >>"
|
||||
|
||||
save_workingdir_cache:
|
||||
parameters:
|
||||
workingdir:
|
||||
type: string
|
||||
default: /Users/distiller/project/workingdir
|
||||
platform:
|
||||
type: string
|
||||
default: ios
|
||||
sdk_version:
|
||||
type: string
|
||||
default: sdk32
|
||||
steps:
|
||||
- save_cache:
|
||||
key: "workingdir-v1-<< parameters.platform >>-<< parameters.sdk_version >>"
|
||||
paths:
|
||||
- "<< parameters.workingdir >>/<< parameters.platform >>/<< parameters.sdk_version >>"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/node:10.15.0
|
||||
- image: circleci/node:8.12
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn
|
||||
- run: yarn lint
|
||||
- run: yarn build
|
||||
test:
|
||||
docker:
|
||||
- image: circleci/node:10.15.0
|
||||
|
||||
unit_tests:
|
||||
executor: nix
|
||||
steps:
|
||||
- checkout
|
||||
- setup
|
||||
- restore_yarn_cache
|
||||
- run: yarn
|
||||
- run: ./test-ci-unit.sh
|
||||
- save_yarn_cache
|
||||
- run:
|
||||
name: Run iOS smoke tests
|
||||
command: ./scripts/tests/runUnitTestsCI.sh
|
||||
|
||||
ios_smoke_tests:
|
||||
executor: mac
|
||||
steps:
|
||||
- install_nix
|
||||
- setup
|
||||
- decrypt_secrets_if_possible
|
||||
- restore_yarn_cache
|
||||
- run: yarn
|
||||
- save_yarn_cache
|
||||
- restore_workingdir_cache:
|
||||
sdk_version: sdk32
|
||||
platform: ios
|
||||
- run:
|
||||
name: Run iOS smoke tests
|
||||
command: ./scripts/tests/runIOSSmokeTestsCI.sh
|
||||
- save_workingdir_cache:
|
||||
workingdir: /Users/distiller/project/workingdir
|
||||
sdk_version: sdk32
|
||||
platform: ios
|
||||
|
||||
android_smoke_tests:
|
||||
executor: android
|
||||
steps:
|
||||
- setup
|
||||
- setup_nix
|
||||
- restore_yarn_cache
|
||||
- run: yarn
|
||||
- save_yarn_cache
|
||||
- restore_workingdir_cache:
|
||||
sdk_version: sdk32
|
||||
platform: android
|
||||
- run:
|
||||
name: Fetch Android shell app
|
||||
command: SDK_VERSION=sdk32 ./scripts/android/fetchRemoteAndroidTarball.sh
|
||||
- run:
|
||||
name: Run Android smoke tests
|
||||
command: |
|
||||
nix-shell nix/android-shell.nix --pure \
|
||||
--run "./scripts/tests/runAndroidSmokeTestsCI.sh"
|
||||
- save_workingdir_cache:
|
||||
workingdir: ~/turtle/workingdir
|
||||
sdk_version: sdk32
|
||||
platform: android
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
checks:
|
||||
jobs:
|
||||
- build
|
||||
- test
|
||||
tests:
|
||||
jobs:
|
||||
- unit_tests
|
||||
- ios_smoke_tests
|
||||
- android_smoke_tests
|
||||
@@ -15,4 +15,6 @@ export NODE_ENV=development
|
||||
|
||||
export EXPO_TURTLE_TOOLS_DIR="$(pwd)/tools"
|
||||
|
||||
PATH_add $EXPO_TURTLE_TOOLS_DIR/bin
|
||||
if [ -z "$EXPO_UNIVERSE_DIR" ]; then
|
||||
PATH_add $EXPO_TURTLE_TOOLS_DIR/bin
|
||||
fi
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"import"
|
||||
],
|
||||
"extends": ["universe", "plugin:import/errors"],
|
||||
"rules":{
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"groups": ["builtin", "external", "parent", "sibling", "index"],
|
||||
"newlines-between": "always"
|
||||
}
|
||||
],
|
||||
"import/no-unresolved": "off"
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"babel-module": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -1,8 +1,13 @@
|
||||
!/src/node_modules
|
||||
|
||||
node_modules
|
||||
yarn-error.log
|
||||
|
||||
/build/
|
||||
/workingdir/*
|
||||
/artifacts/*
|
||||
shell.apk
|
||||
node_modules
|
||||
.secrets*
|
||||
|
||||
*.swp
|
||||
*.swo
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"commit": false,
|
||||
"tag": true,
|
||||
"push": true,
|
||||
"requireUpstream": false,
|
||||
"tagName": "v${version}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"npm": {
|
||||
"tag": "latest"
|
||||
},
|
||||
"git": {
|
||||
"requireUpstream": false,
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"github": {
|
||||
"release": true
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ LOGGER_LEVEL="debug"
|
||||
API_PROTOCOL="http"
|
||||
API_HOSTNAME="localhost"
|
||||
API_PORT="3000"
|
||||
LOGGLY_TOKEN=""
|
||||
SENTRY_DSN=""
|
||||
ENVIRONMENT="development"
|
||||
REDIS_URL="redis://localhost:6379/0"
|
||||
|
||||
+80
-3
@@ -5,19 +5,96 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
### Changed
|
||||
- Updated iOS shell app for SDK 32 to fix AppAuth issue
|
||||
|
||||
## [0.6.0] - 2019-04-09
|
||||
### Changed
|
||||
- Removed Branch module from Android shell apps for all SDK versions
|
||||
|
||||
## [0.5.15] - 2019-04-03
|
||||
### Added
|
||||
- Turtle smoke tests are now being executed on CircleCI from the context of this repository.
|
||||
### Changed
|
||||
- Updated iOS shell app for SDK 32 (fixes [this loading issue for slow connections](https://github.com/expo/expo/issues/3574)).
|
||||
- Minor infra changes (doesn't affect users).
|
||||
- Error logs format - error objects are now passed in an object being passed as the first argument to logger function.
|
||||
### Removed
|
||||
- Sending logs to loggly.
|
||||
|
||||
## [0.5.14] - 2019-03-22
|
||||
### Added
|
||||
- `--config` parameter for turtle-cli commands, which allows to specify a custom path to app.json file
|
||||
### Changed
|
||||
- Updated iOS shell app for SDK 32 (fixes [this notifications issue](https://github.com/expo/expo/issues/3663)).
|
||||
|
||||
## [0.5.13] - 2019-03-12
|
||||
### Added
|
||||
- Android SDK version support validation.
|
||||
### Changed
|
||||
- iOS Enterprise builds don't compile bitcode now.
|
||||
|
||||
## [0.5.12] - 2019-02-26
|
||||
### Added
|
||||
- Google Cloud logger (as a bunyan stream).
|
||||
### Changed
|
||||
- Android builder passes the manifest from a build job message if it's available.
|
||||
|
||||
## [0.5.11] - 2019-02-22
|
||||
### Added
|
||||
- Builder for iOS client apps.
|
||||
|
||||
## [0.5.10] - 2019-02-19
|
||||
### Added
|
||||
- Register Turtle version in Redis.
|
||||
### Changed
|
||||
- Update Android shell app for SDK 32 to fix [update](https://github.com/expo/expo/issues/3504) and [back button](https://github.com/expo/expo/issues/1786) issues.
|
||||
|
||||
## [0.5.9] - 2019-02-06
|
||||
- Update iOS shell app for SDK 32 (fixes bare notifications [issue](https://github.com/expo/expo/issues/3223)).
|
||||
|
||||
## [0.5.7] - 2019-01-31
|
||||
- Update Android shell app for SDK 32 to fix bundled assets issue.
|
||||
|
||||
## [0.5.6] - 2019-01-25
|
||||
- Update iOS shell app for SDK 30, 31 and 32 (built with xcode 10.1).
|
||||
|
||||
## [0.5.5] - 2019-01-24
|
||||
### Added
|
||||
- Sending build duration in AWS SQS messages (of `FINISHED` and `ERRORED` types).
|
||||
|
||||
## [0.5.4] - 2019-01-22
|
||||
### Changed
|
||||
- Update iOS shell app for SDK32 to [`expo/expo#0847c10`](https://github.com/expo/expo/commit/0847c1073d38c366866f3d9279ddd662da47dc9b), i.e. v2.10.3 with fixed WebView navigation scheme and upgraded Facebook SDK.
|
||||
|
||||
## [0.5.3] - 2019-01-21
|
||||
### Fixed
|
||||
- Building Android standalone apps ([not accepted license issue](https://github.com/expo/expo/issues/3266)) - updated Android build tools (`build-tools;28.0.3`, `platforms;android-28`).
|
||||
|
||||
## [0.5.2] - 2019-01-18
|
||||
### Added
|
||||
- The link (in README) to the repository containing `turtle-cli` usage example.
|
||||
- [Contributing guide](CONTRIBUTING.md).
|
||||
- [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||
### Removed
|
||||
- `eslint`
|
||||
### Changed
|
||||
- Fix all `tslint` errors.
|
||||
|
||||
## [0.5.1] - 2019-01-16
|
||||
### Changed
|
||||
- Update Android shell app for SDK32 that includes react-native-screens@1.0.0-alpha.22
|
||||
- Update Android shell app for SDK32 that includes `[email protected]`.
|
||||
|
||||
## [0.5.0] - 2019-01-16
|
||||
### Added
|
||||
- Turtle version is added to onFinish and onError build messages.
|
||||
- Turtle version is added to the `onFinish` and `onError` build messages.
|
||||
### Changed
|
||||
- A version property value being sent to the queue. It's now the version number
|
||||
from `package.json` instead of `new`/`old` indicator.
|
||||
### Removed
|
||||
- `--push-p12-path` parameter for `turtle build:ios` command,
|
||||
a Push Notifications Certificate is no longer required to perform a successful
|
||||
iOS standalone app build.
|
||||
- new/old Turtle version build argument
|
||||
|
||||
## [0.4.3] - 2019-01-09
|
||||
### Changed
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Contributing to Turtle CLI
|
||||
|
||||
## How to contribute
|
||||
You can contribute to Turtle CLI in various ways, including:
|
||||
- [Reporting bugs or issues](https://github.com/expo/turtle/issues/new) on GitHub. Please make sure to fill in all the details suggested by the template to make sure the issue can be addressed as quickly as possible.
|
||||
- [Submitting improvements to the documentation](https://github.com/expo/expo/tree/master/docs). Updates, enhancements, new guides, spelling fixes…
|
||||
- Helping other people on [Expo Forums](https://forums.expo.io/) and on [Expo Developers Slack](http://slack.expo.io).
|
||||
- Looking at existing [issues](https://github.com/expo/turtle/issues) and adding more information, particularly helping to reproduce the issues.
|
||||
- [Submitting a pull request](#submitting-a-pull-request) with a bug fix or an improvement.
|
||||
|
||||
## Turtle CLI repository
|
||||
|
||||
### Codebase
|
||||
This repository contains the codebase for the `turtle-cli` NPM package. The same codebase is used to run standalone app builders on the Expo servers.
|
||||
|
||||
### Branches
|
||||
The `master` branch of the repository should be kept releasable at any time. This way we can continuously release fixes and improvements without costly managing of different branches and issues will be noticed and fixed quickly. This also ensures other contributors can check out the latest version from GitHub and work on it with minimal disruption from other features in progress.
|
||||
|
||||
## Setting up the repository for development
|
||||
*TODO*
|
||||
|
||||
## Submitting a pull request
|
||||
To submit a pull request:
|
||||
1. Fork the [repository](https://github.com/expo/turtle) and create a feature branch. (Existing contributors can create feature branches without forking. Prefix the branch name with `@your-github-username/`.)
|
||||
2. Write the description of your pull request. Make sure to include a test plan and test your changes.
|
||||
3. Make sure all tests pass on CircleCI.
|
||||
4. Wait for a review and adjust the code if necessary.
|
||||
|
||||
## Publishing a release
|
||||
- To release a new version of `turtle-cli`, run `yarn release` command.
|
||||
- If you wish to release a new *beta* version of `turtle-cli`, run `yarn release:beta` instead.
|
||||
- Update [Changelog](CHANGELOG.md).
|
||||
- In a terminal, `cd` into [expo/turtle-cli-example](https://github.com/expo/turtle-cli-example) repository, run `yarn update-turtle-cli-version` (this script updates `turtle-cli` version number in CI configuration files and commits the changes) and then run `git push`. Thanks!
|
||||
+13
-16
@@ -36,30 +36,34 @@ RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-
|
||||
# Download and untar SDK
|
||||
ENV ANDROID_HOME /usr/local/android-sdk-linux
|
||||
ENV ANDROID_SDK /usr/local/android-sdk-linux
|
||||
ENV ANDROID_SDK_URL https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
|
||||
ENV ANDROID_SDK_URL https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip
|
||||
RUN mkdir -p ${ANDROID_HOME} && \
|
||||
curl -L "${ANDROID_SDK_URL}" > ${ANDROID_HOME}/sdk.zip && \
|
||||
cd ${ANDROID_HOME} && \
|
||||
unzip -qq sdk.zip && \
|
||||
rm sdk.zip && \
|
||||
# prevents warnings about missing repo config
|
||||
mkdir -p ${HOME}/.android && \
|
||||
touch ${HOME}/.android/repositories.cfg
|
||||
|
||||
ENV PATH ${ANDROID_HOME}/platform-tools:${PATH}
|
||||
ENV PATH ${ANDROID_HOME}/tools:${PATH}
|
||||
ENV PATH ${ANDROID_HOME}/tools/bin:${PATH}
|
||||
ENV PATH ${ANDROID_HOME}/build-tools/25.0.0/:${PATH}
|
||||
ENV PATH ${ANDROID_HOME}/build-tools/28.0.3/:${PATH}
|
||||
|
||||
RUN yes | sdkmanager --licenses > /dev/null
|
||||
|
||||
# Install Android SDK components
|
||||
RUN sdkmanager \
|
||||
"platform-tools" \
|
||||
RUN yes | sdkmanager \
|
||||
"platforms;android-23" \
|
||||
"build-tools;25.0.0" \
|
||||
"platforms;android-24" \
|
||||
"platforms;android-25" \
|
||||
"platforms;android-26" \
|
||||
"platforms;android-27" \
|
||||
"platforms;android-28"
|
||||
RUN yes | sdkmanager "platform-tools"
|
||||
RUN yes | sdkmanager "build-tools;28.0.3"
|
||||
RUN yes | sdkmanager \
|
||||
"extras;android;m2repository" \
|
||||
"extras;google;m2repository"
|
||||
"extras;google;m2repository" \
|
||||
"extras;google;google_play_services"
|
||||
|
||||
# Install Android NDK
|
||||
ENV ANDROID_NDK_VERSION android-ndk-r10e
|
||||
@@ -98,13 +102,6 @@ ENV PATH ${GRADLE_HOME}/bin:$PATH
|
||||
# Install Gulp
|
||||
RUN npm install -g gulp-cli
|
||||
|
||||
# Install Git
|
||||
RUN echo 'deb http://http.debian.net/debian wheezy-backports main' >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get -t wheezy-backports install git-core && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ADD . /app
|
||||
|
||||
RUN for SDK_VERSION in `ls /app/workingdir/android/`; do \
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
# Turtle CLI
|
||||
|
||||
[](https://github.com/expo/turtle/blob/master/LICENSE)
|
||||
[](https://rubygems.org/gems/fastlane)
|
||||
[](https://circleci.com/gh/expo/turtle)
|
||||
[](https://npmjs.com/package/turtle-cli)
|
||||
[](https://github.com/expo/turtle/blob/master/LICENSE)
|
||||
|
||||
[](http://slack.expo.io)
|
||||
[](https://forums.expo.io/)
|
||||
|
||||
Turtle CLI is a command line interface for building Expo standalone apps.
|
||||
You can use it both on your CI and your private computer.
|
||||
|
||||
### Glossary
|
||||
- [Changelog](CHANGELOG.md)
|
||||
- [Contributing to Turtle CLI](CONTRIBUTING.md)
|
||||
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
||||
|
||||
## Glossary
|
||||
- **Turtle** - script which takes a shell app, fills it with user's code and produces a standalone app
|
||||
- **Shell app** — precompiled (in case of iOS builds) Expo app which doesn't have user's code
|
||||
- **Standalone app** - .ipa/.apk file, a user uploads standalone apps to the Google Play Store/Apple App Store
|
||||
|
||||
## Installation
|
||||
|
||||
[Installation instructions and documentation here.](https://docs.expo.io/versions/latest/distribution/turtle-cli)
|
||||
|
||||
## Usage example
|
||||
See the [expo/turtle-cli-example](https://github.com/expo/turtle-cli-example) repository to learn how to use Turtle CLI in order to build Expo standalone apps on your CI.
|
||||
|
||||
## Do you need help?
|
||||
- Make sure you've read [the Expo Docs](https://docs.expo.io/), especially [Building Standalone Apps on Your CI
|
||||
](https://docs.expo.io/versions/latest/distribution/turtle-cli) and [Building Standalone Apps](https://docs.expo.io/versions/latest/distribution/building-standalone-apps) guides.
|
||||
- Search for existing GitHub issues [in this repository](https://github.com/expo/turtle/issues) and in [expo/expo](https://github.com/expo/expo/issues) repository.
|
||||
- Ask for help in **one** of the following ways:
|
||||
* create [a GitHub issue](https://github.com/expo/turtle/issues/new).
|
||||
* create a new topic on [Expo Forums](https://forums.expo.io/).
|
||||
* ask a question on [Expo Developers Slack](http://slack.expo.io).
|
||||
|
||||
## License
|
||||
The Turtle CLI source code is made available under the [MIT license](LICENSE).
|
||||
Some of the dependencies are licensed differently, with the BSD license, for example.
|
||||
@@ -9,6 +9,6 @@
|
||||
"^.+\\.ts$": "ts-jest"
|
||||
},
|
||||
"testRegex": "(test|spec)\\.ts$",
|
||||
"moduleFileExtensions": ["ts", "js"],
|
||||
"moduleFileExtensions": ["ts", "js", "json"],
|
||||
"verbose": true
|
||||
}
|
||||
@@ -9,6 +9,6 @@
|
||||
"^.+\\.ts$": "ts-jest"
|
||||
},
|
||||
"testMatch": [ "**/__smoke__/*.test.ts" ],
|
||||
"moduleFileExtensions": ["ts", "js"],
|
||||
"moduleFileExtensions": ["ts", "js", "json"],
|
||||
"verbose": true
|
||||
}
|
||||
@@ -9,6 +9,6 @@
|
||||
"^.+\\.ts$": "ts-jest"
|
||||
},
|
||||
"testMatch": [ "**/__tests__/*.ts" ],
|
||||
"moduleFileExtensions": ["ts", "js"],
|
||||
"moduleFileExtensions": ["ts", "js", "json"],
|
||||
"verbose": true
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"allowSyntheticDefaultImports": false,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"turtle/*": ["src/*"],
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "build"]
|
||||
}
|
||||
@@ -50,8 +50,6 @@ spec:
|
||||
value: exp-shell-app-assets
|
||||
- name: AWS_S3_REGION
|
||||
value: us-west-1
|
||||
- name: LOGGLY_SUBDOMAIN
|
||||
value: exponent
|
||||
- name: TURTLE_TEMPORARY_FILES_DIR
|
||||
value: /tmp/turtle
|
||||
# vars from k8s secrets
|
||||
@@ -100,11 +98,6 @@ spec:
|
||||
secretKeyRef:
|
||||
name: turtle-js-config
|
||||
key: aws_cloudwatch_region
|
||||
- name: LOGGLY_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: turtle-js-config
|
||||
key: loggly_token
|
||||
- name: REDIS_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
self: super:
|
||||
|
||||
{
|
||||
nodejs = super.nodejs-8_x;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
with (import ./default.nix {});
|
||||
|
||||
let
|
||||
|
||||
ndk = androidenv.androidndk_17c.override { fullNDK = true; };
|
||||
ndk_root = "${ndk}/libexec/${ndk.name}";
|
||||
sdk_path = if stdenv.isDarwin
|
||||
then "/Library/Android/sdk"
|
||||
else "/Android/Sdk"; # intentional Capital S
|
||||
|
||||
in
|
||||
|
||||
mkShell rec {
|
||||
|
||||
LANG="en_US.UTF-8";
|
||||
JAVA_HOME=openjdk8;
|
||||
|
||||
ANDROID_SDK_ROOT=builtins.getEnv("HOME") + sdk_path;
|
||||
ANDROID_NDK_ROOT="${ANDROID_SDK_ROOT}/ndk-bundle";
|
||||
ANDROID_HOME=ANDROID_SDK_ROOT;
|
||||
ANDROID_NDK=ANDROID_NDK_ROOT;
|
||||
|
||||
nativeBuildInputs = [
|
||||
awscli
|
||||
curl
|
||||
git
|
||||
nodejs-8_x
|
||||
openjdk8
|
||||
yarn
|
||||
];
|
||||
|
||||
passthru = { inherit ndk; };
|
||||
|
||||
shellHook = ''
|
||||
${./install-ndk-17c.sh} ${ndk} ${ndk_root}
|
||||
yes | ${androidenv.androidsdk_9_0}/bin/sdkmanager --licenses --sdk_root="$ANDROID_SDK_ROOT"
|
||||
yes | ${androidenv.androidsdk_9_0}/bin/sdkmanager --sdk_root="$ANDROID_SDK_ROOT" "platforms;android-28" | grep -v =
|
||||
yes | ${androidenv.androidsdk_9_0}/bin/sdkmanager --sdk_root="$ANDROID_SDK_ROOT" "build-tools;28.0.3" | grep -v =
|
||||
${lib.optionalString stdenv.isLinux ''
|
||||
for dep in lib lib64; do
|
||||
if [ -L /$dep ] || [ ! -e /$dep ]; then
|
||||
echo "Creating /$dep"
|
||||
ln -s ${stdenv.cc.libc}/$dep /$dep
|
||||
else
|
||||
echo "Using existing /$dep"
|
||||
fi
|
||||
done
|
||||
''}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
let
|
||||
pin = builtins.fromJSON (builtins.readFile ./nixpkgs.json);
|
||||
|
||||
nixpkgs = builtins.fetchTarball {
|
||||
url = "https://github.com/NixOS/nixpkgs-channels/archive/${pin.rev}.tar.gz";
|
||||
inherit (pin) sha256;
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
{ ... } @ args:
|
||||
|
||||
import nixpkgs (args // {
|
||||
config = {
|
||||
allowUnfree = true;
|
||||
android_sdk.accept_license = true;
|
||||
};
|
||||
overlays = [ (import ./all-packages.nix) ];
|
||||
})
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
scriptdir="$(dirname ${BASH_SOURCE[0]})"
|
||||
shellnix="$scriptdir/android-shell.nix"
|
||||
|
||||
ndk=${1:-$(nix-build "$shellnix" -A ndk --no-out-link)}
|
||||
ndkRoot=${2:-$(nix eval --file "$shellnix" --raw ANDROID_NDK_ROOT)}
|
||||
|
||||
mkdir -p "$ANDROID_NDK_ROOT" # Create full path if necessary
|
||||
|
||||
# Replace any existing link or directory with link
|
||||
rm -r "$ANDROID_NDK_ROOT"
|
||||
ln -sn "$ndkRoot" "$ANDROID_NDK_ROOT"
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"url": "https://github.com/NixOS/nixpkgs-channels.git",
|
||||
"rev": "80738ed9dc0ce48d7796baed5364eef8072c794d",
|
||||
"date": "2018-11-17T11:18:10+01:00",
|
||||
"sha256": "0anmvr6b47gbbyl9v2fn86mfkcwgpbd5lf0yf3drgm8pbv57c1dc",
|
||||
"fetchSubmodules": false
|
||||
}
|
||||
+37
-19
@@ -1,23 +1,23 @@
|
||||
{
|
||||
"name": "turtle-cli",
|
||||
"version": "0.5.1",
|
||||
"version": "0.6.0",
|
||||
"description": "The command-line tool for building standalone Expo apps",
|
||||
"preferGlobal": true,
|
||||
"scripts": {
|
||||
"start": "EXPENV_OVERRIDES=local-common,local expenv gulp",
|
||||
"start": "EXPENV_OVERRIDES=local-common,local,local-queue expenv gulp",
|
||||
"start:ios": "PLATFORM=ios yarn start",
|
||||
"start:android": "PLATFORM=android yarn start",
|
||||
"watch": "EXPENV_OVERRIDES=local-common,local expenv gulp watch:source",
|
||||
"watch": "EXPENV_OVERRIDES=local-common,local,local-queue expenv gulp watch:source",
|
||||
"bin:turtle": "./bin/turtle.js",
|
||||
"lint": "eslint src",
|
||||
"lint": "tslint -c tslint.json 'src/**/*.ts'",
|
||||
"lint:fix": "yarn lint --fix",
|
||||
"prepare": "yarn build",
|
||||
"build": "gulp build:server",
|
||||
"build:production": "./scripts/build.sh",
|
||||
"build:android-tarball:local": "./scripts/android/buildAndroidTarballLocally.sh",
|
||||
"init-workingdir:local": "./scripts/initLocalWorkingdir.sh",
|
||||
"init-workingdir:remote": "yarn init-workingdir:remote-android && yarn init-workingdir:remote-ios",
|
||||
"init-workingdir:remote-android": "./scripts/android/fetchRemoteAndroidTarball.sh",
|
||||
"init-workingdir:remote-ios": "./scripts/ios/fetchRemoteIosTarball.sh",
|
||||
"secrets:init-private": "./scripts/initPrivateSecrets.sh",
|
||||
"set-workingdir:remote": "./scripts/setWorkingdirMode.sh remote",
|
||||
"set-workingdir:local": "./scripts/setWorkingdirMode.sh local",
|
||||
"docker:pull": "retry5 gcloud docker -- pull gcr.io/exponentjs/turtle-android:latest",
|
||||
"docker:pull:tag": "retry5 gcloud docker -- pull gcr.io/exponentjs/turtle-android:${TAG}",
|
||||
"docker:build": "yarn build:production android && gcloud docker -- build -t gcr.io/exponentjs/turtle-android:latest -t gcr.io/exponentjs/turtle-android:${TAG} .",
|
||||
@@ -27,17 +27,29 @@
|
||||
"test:integration": "yarn test --config jest/integration-config.json",
|
||||
"test:unit": "yarn test --config jest/unit-config.json",
|
||||
"test:smoke": "yarn test --config jest/smoke-config.json --forceExit",
|
||||
"test:smoke:android": "yarn test:smoke --testNamePattern=android",
|
||||
"test:smoke:android": "PLATFORM=android yarn test:smoke --testNamePattern=android",
|
||||
"test:smoke:ios": "PLATFORM=ios yarn test:smoke --testNamePattern=iOS",
|
||||
"secrets:init-private": "./scripts/initPrivateSecrets.sh",
|
||||
"release": "./scripts/release.sh",
|
||||
"release:beta": "./scripts/releaseBeta.sh",
|
||||
"ssh:android:pods-names": "./scripts/ssh/findAndroidPods.sh",
|
||||
"ssh:android:connect": "./scripts/ssh/connectToAndroidPod.sh",
|
||||
"ssh:ios:ips": "./scripts/ssh/findIosIps.sh",
|
||||
"set-priorities": "yarn set-priorities:local",
|
||||
"set-priorities:local": "EXPENV_OVERRIDES=local-common,local expenv babel-node scripts/setConfigurations.js",
|
||||
"set-priorities:local": "EXPENV_OVERRIDES=local-common,local,local-queue expenv babel-node scripts/setConfigurations.js",
|
||||
"set-priorities:staging": "expenv babel-node scripts/setConfigurations.js",
|
||||
"set-priorities:production": "EXPENV_OVERRIDES=prod expenv babel-node scripts/setConfigurations.js"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.ts": [
|
||||
"yarn lint:fix",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"bin": {
|
||||
"turtle": "./bin/turtle.js"
|
||||
},
|
||||
@@ -60,6 +72,9 @@
|
||||
"url": "https://github.com/expo/turtle/issues"
|
||||
},
|
||||
"homepage": "https://github.com/expo/turtle#readme",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/decompress": "^4.2.3",
|
||||
"@types/fs-extra": "^5.0.4",
|
||||
@@ -75,13 +90,9 @@
|
||||
"@types/which": "^1.3.1",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^8.2.3",
|
||||
"babel-jest": "^23.6.0",
|
||||
"babel-plugin-module-resolver": "^3.1.1",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-universe": "^1.0.7",
|
||||
"eslint-import-resolver-babel-module": "^4.0.0",
|
||||
"gulp": "github:gulpjs/gulp#4.0",
|
||||
"gulp-babel": "^7.0.1",
|
||||
"gulp-changed": "^3.2.0",
|
||||
@@ -89,22 +100,26 @@
|
||||
"gulp-plumber": "^1.2.0",
|
||||
"gulp-sourcemaps": "^2.6.4",
|
||||
"gulp-typescript": "^4.0.2",
|
||||
"husky": "^1.3.1",
|
||||
"jest": "^23.6.0",
|
||||
"lint-staged": "^8.1.5",
|
||||
"nock": "^10.0.1",
|
||||
"nodemon": "^1.18.7",
|
||||
"release-it": "^10.0.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"source-map-loader": "^0.2.3",
|
||||
"ts-jest": "^23.10.4",
|
||||
"tslint": "^5.10.0",
|
||||
"typescript": "^3.0.1",
|
||||
"typescript-eslint-parser": "^21.0.2"
|
||||
"tslint-eslint-rules": "^5.4.0",
|
||||
"typescript": "^3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/spawn-async": "^1.4.0",
|
||||
"@google-cloud/logging-bunyan": "^0.10.1",
|
||||
"async-retry": "^1.2.1",
|
||||
"aws-sdk": "^2.226.1",
|
||||
"bunyan": "^1.8.12",
|
||||
"bunyan-debug-stream": "^1.0.8",
|
||||
"bunyan-loggly": "^1.3.4",
|
||||
"bunyan-sentry-stream": "^1.2.1",
|
||||
"commander": "^2.15.1",
|
||||
"copy": "^0.3.1",
|
||||
@@ -123,6 +138,9 @@
|
||||
"tar": "^4.4.6",
|
||||
"uuid": "^3.2.1",
|
||||
"which": "^1.3.1",
|
||||
"xdl": "52.0.14-alpha.6+371be041"
|
||||
"xdl": "53.1.6-alpha.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@expo/traveling-fastlane-darwin": "^1.8.1"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -xeo pipefail
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../.. && pwd )"
|
||||
ARTIFACTS_DIR="$ROOT_DIR/artifacts"
|
||||
WORKING_DIR="$ROOT_DIR/workingdir/android"
|
||||
ANDROID_SHELL_TARBALLS_DIRECTORY="$ROOT_DIR/shellTarballs/android"
|
||||
|
||||
mkdir -p $ARTIFACTS_DIR
|
||||
|
||||
for SDK_VERSION in `ls $ANDROID_SHELL_TARBALLS_DIRECTORY`; do
|
||||
mkdir -p $WORKING_DIR/$SDK_VERSION
|
||||
TARBALL_URI=$(head -n 1 $ANDROID_SHELL_TARBALLS_DIRECTORY/$SDK_VERSION)
|
||||
run expo.s3env --keep AWS_ACCESS_KEY_ID --keep AWS_SECRET_ACCESS_KEY --command retry5 aws s3 cp $TARBALL_URI $ARTIFACTS_DIR/$SDK_VERSION.tar.gz --no-progress
|
||||
tar zxf $ARTIFACTS_DIR/$SDK_VERSION.tar.gz -C $WORKING_DIR/$SDK_VERSION
|
||||
rm -rf $TARBALL_URI $ARTIFACTS_DIR/$SDK_VERSION.tar.gz
|
||||
done
|
||||
$ROOT_DIR/scripts/fetchRemoteTarballs.sh android
|
||||
+8
-1
@@ -2,6 +2,8 @@
|
||||
|
||||
set -xeo pipefail
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Please specify a platform to build"
|
||||
exit 1
|
||||
@@ -11,7 +13,12 @@ PLATFORM=$1
|
||||
|
||||
yarn
|
||||
yarn run build
|
||||
yarn init-workingdir:remote-$PLATFORM
|
||||
if [ "$PLATFORM" = "android" ]; then
|
||||
$ROOT_DIR/scripts/android/fetchRemoteAndroidTarball.sh
|
||||
elif [ "$PLATFORM" = "ios" ]; then
|
||||
$ROOT_DIR/scripts/ios/fetchRemoteIosTarball.sh
|
||||
fi
|
||||
|
||||
|
||||
if [[ -z "${TARBALL}" ]]; then
|
||||
echo "TARBALL is not set, building tarball skipped"
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses > /dev/null
|
||||
$ANDROID_HOME/tools/bin/sdkmanager \
|
||||
"platform-tools" \
|
||||
"platforms;android-23" \
|
||||
"build-tools;25.0.0" \
|
||||
SDKMANAGER=$ANDROID_HOME/tools/bin/sdkmanager
|
||||
|
||||
yes | $SDKMANAGER --licenses > /dev/null
|
||||
yes | $SDKMANAGER "platforms;android-28"
|
||||
yes | $SDKMANAGER "platform-tools"
|
||||
yes | $SDKMANAGER "build-tools;28.0.3"
|
||||
yes | $SDKMANAGER \
|
||||
"extras;android;m2repository" \
|
||||
"extras;google;m2repository"
|
||||
"extras;google;m2repository" \
|
||||
"extras;google;google_play_services"
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -xeo pipefail
|
||||
|
||||
PLATFORM=$1
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
||||
ARTIFACTS_DIR="$ROOT_DIR/artifacts"
|
||||
WORKING_DIR="$ROOT_DIR/workingdir/$PLATFORM"
|
||||
SHELL_TARBALLS_DIRECTORY="$ROOT_DIR/shellTarballs/$PLATFORM"
|
||||
|
||||
mkdir -p $ARTIFACTS_DIR
|
||||
|
||||
convert_s3_to_https() {
|
||||
BUCKET=`echo $1 | sed 's/s3:\/\/\([^/]*\).*/\1/'`
|
||||
KEY=`echo $1 | sed 's/s3:\/\/[^/]*\/\(.*\)/\1/'`
|
||||
echo "https://s3.amazonaws.com/$BUCKET/$KEY"
|
||||
}
|
||||
|
||||
download_shell_app() {
|
||||
SDK_VERSION=$1
|
||||
mkdir -p $WORKING_DIR/$SDK_VERSION
|
||||
TARBALL_S3_URL=$(head -n 1 $SHELL_TARBALLS_DIRECTORY/$SDK_VERSION)
|
||||
TARBALL_HTTPS_URL=$(convert_s3_to_https $TARBALL_S3_URL)
|
||||
curl -s $TARBALL_HTTPS_URL -o $ARTIFACTS_DIR/$SDK_VERSION.tar.gz
|
||||
tar zxf $ARTIFACTS_DIR/$SDK_VERSION.tar.gz -C $WORKING_DIR/$SDK_VERSION
|
||||
rm -rf $ARTIFACTS_DIR/$SDK_VERSION.tar.gz
|
||||
}
|
||||
|
||||
if [[ -z "$SDK_VERSION" ]]; then
|
||||
for SDK_VERSION in `ls $SHELL_TARBALLS_DIRECTORY`; do
|
||||
download_shell_app $SDK_VERSION
|
||||
done
|
||||
else
|
||||
if [ ! -d "$WORKING_DIR/$SDK_VERSION" ]; then
|
||||
download_shell_app $SDK_VERSION
|
||||
fi
|
||||
fi
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
ensure_link_exists() {
|
||||
if [ ! -L "$2" ]; then
|
||||
ln -s $1 $2
|
||||
echo "$2 link created"
|
||||
fi
|
||||
}
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
||||
WORKINGDIR="$ROOT_DIR/workingdir/local"
|
||||
|
||||
if [ ! -d "$WORKINGDIR" ]; then
|
||||
mkdir -p $WORKINGDIR
|
||||
echo "$WORKINGDIR directory created"
|
||||
fi
|
||||
|
||||
LINKS=( "template-files" "package.json" "android" "expokit-npm-package" "packages" "client-builds" "cpp" "exponent-view-template" "ios" "shellAppBase-builds" "shellAppWorkspaces")
|
||||
|
||||
for LINKNAME in ${LINKS[@]}; do
|
||||
ensure_link_exists "$EXPO_UNIVERSE_DIR/exponent/$LINKNAME" "$WORKINGDIR/$LINKNAME"
|
||||
done
|
||||
@@ -1,5 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ -z "$EXPO_UNIVERSE_DIR" ]; then
|
||||
echo "You need to specify EXPO_UNIVERSE_DIR to use this script. Alternatively you can create .secrets.local-queue file yourself with AWS_SQS_ANDROID_QUEUE_URL, AWS_SQS_IOS_QUEUE_URL and AWS_SQS_OUT_QUEUE_URL variables."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Please specify a nickname you used to create AWS SQS queues"
|
||||
exit 1
|
||||
@@ -12,12 +17,9 @@ ANDROID=`cd $TERRAFORM_DIR; terraform state show module.turtle_queues_local_$1.a
|
||||
IOS=`cd $TERRAFORM_DIR; terraform state show module.turtle_queues_local_$1.aws_sqs_queue.jobs_ios_in | head -1 | cut -d"=" -f2 | xargs`
|
||||
OUT=`cd $TERRAFORM_DIR; terraform state show module.turtle_queues_local_$1.aws_sqs_queue.jobs_out | head -1 | cut -d"=" -f2 | xargs`
|
||||
|
||||
cat << EOF > $TURTLE_DIR/.secrets.local
|
||||
cat << EOF > $TURTLE_DIR/.secrets.local-queue
|
||||
AWS_SQS_ANDROID_QUEUE_URL="$ANDROID"
|
||||
AWS_SQS_IOS_QUEUE_URL="$IOS"
|
||||
AWS_SQS_OUT_QUEUE_URL="$OUT"
|
||||
TURTLE_FAKE_UPLOAD="1"
|
||||
TURTLE_FAKE_UPLOAD_DIR="/Users/$USER/Downloads"
|
||||
TURTLE_WORKING_DIR_PATH="$EXPO_UNIVERSE_DIR/server/turtle/workingdir"
|
||||
TURTLE_USE_LOCAL_WORKING_DIR="1"
|
||||
EOF
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -xeo pipefail
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../.. && pwd )"
|
||||
ARTIFACTS_DIR="$ROOT_DIR/artifacts"
|
||||
WORKING_DIR="$ROOT_DIR/workingdir/ios"
|
||||
IOS_SHELL_TARBALLS_DIRECTORY="$ROOT_DIR/shellTarballs/ios"
|
||||
|
||||
mkdir -p $ARTIFACTS_DIR
|
||||
|
||||
for SDK_VERSION in `ls $IOS_SHELL_TARBALLS_DIRECTORY`; do
|
||||
mkdir -p $WORKING_DIR/$SDK_VERSION
|
||||
TARBALL_URI=$(head -n 1 $IOS_SHELL_TARBALLS_DIRECTORY/$SDK_VERSION)
|
||||
run expo.s3env --keep AWS_ACCESS_KEY_ID --keep AWS_SECRET_ACCESS_KEY --command retry5 aws s3 cp $TARBALL_URI $ARTIFACTS_DIR/$SDK_VERSION.tar.gz --no-progress
|
||||
tar zxf $ARTIFACTS_DIR/$SDK_VERSION.tar.gz -C $WORKING_DIR/$SDK_VERSION
|
||||
rm -rf $TARBALL_URI $ARTIFACTS_DIR/$SDK_VERSION.tar.gz
|
||||
done
|
||||
$ROOT_DIR/scripts/fetchRemoteTarballs.sh ios
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
||||
|
||||
$ROOT_DIR/node_modules/.bin/release-it
|
||||
echo
|
||||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
|
||||
echo "!!!! PLEASE REMEMBER TO UPDATE CHANGELOG !!!!"
|
||||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
||||
|
||||
$ROOT_DIR/node_modules/.bin/release-it -c $ROOT_DIR/.release-it.beta.json --preRelease=beta
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
||||
|
||||
DOWNLOAD_DIR="${EXPO_DOWNLOAD_DIR:-$HOME/Downloads}"
|
||||
|
||||
mkdir -p $DOWNLOAD_DIR
|
||||
|
||||
if [ ! -f "$ROOT_DIR/.secrets.local-queue" ]; then
|
||||
echo "You need to run \"yarn secrets:init-private [SQS_NAME]\" or create .secrets.local-queue file yourself with AWS_SQS_ANDROID_QUEUE_URL, AWS_SQS_IOS_QUEUE_URL and AWS_SQS_OUT_QUEUE_URL variables."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
setup_local() {
|
||||
ensure_link_exists() {
|
||||
if [ ! -L "$2" ]; then
|
||||
ln -s $1 $2
|
||||
echo "$2 link created"
|
||||
fi
|
||||
}
|
||||
WORKINGDIR="$ROOT_DIR/workingdir/local"
|
||||
|
||||
if [[ -d $WORKINGDIR ]]; then
|
||||
echo "Local workingdir already prepared"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -z "$EXPO_UNIVERSE_DIR" ]]; then
|
||||
echo "EXPO_UNIVERSE_DIR needs to be set"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
cd $EXPO_UNIVERSE_DIR/exponent && yarn
|
||||
cd $EXPO_UNIVERSE_DIR/exponent/tools-public && yarn && ./generate-dynamic-macros-android.sh
|
||||
|
||||
mkdir -p $WORKINGDIR
|
||||
echo "$WORKINGDIR directory created"
|
||||
|
||||
LINKS=("template-files" "package.json" "android" "expokit-npm-package" "packages" "client-builds" "cpp" "exponent-view-template" "ios" "shellAppBase-builds" "shellAppWorkspaces")
|
||||
|
||||
for LINKNAME in ${LINKS[@]}; do
|
||||
ensure_link_exists "$EXPO_UNIVERSE_DIR/exponent/$LINKNAME" "$WORKINGDIR/$LINKNAME"
|
||||
done
|
||||
}
|
||||
|
||||
setup_remote() {
|
||||
if [[ -d $ROOT_DIR/workingdir/android && -d $ROOT_DIR/workingdir/ios ]]; then
|
||||
echo "Shell apps already fetched"
|
||||
else
|
||||
echo "Fetching shell apps"
|
||||
if [ "$PLATFORM" != 'ios' ]; then
|
||||
$ROOT_DIR/scripts/android/fetchRemoteAndroidTarball.sh
|
||||
fi
|
||||
if [ "$PLATFORM" != 'android' ]; then
|
||||
$ROOT_DIR/scripts/ios/fetchRemoteIosTarball.sh
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
switch_local() {
|
||||
cat << EOF > $ROOT_DIR/.secrets.local
|
||||
TURTLE_FAKE_UPLOAD="1"
|
||||
TURTLE_FAKE_UPLOAD_DIR="$HOME/Downloads"
|
||||
TURTLE_WORKING_DIR_PATH="$ROOT_DIR/workingdir"
|
||||
TURTLE_USE_LOCAL_WORKING_DIR="1"
|
||||
EOF
|
||||
}
|
||||
|
||||
switch_remote() {
|
||||
cat << EOF > $ROOT_DIR/.secrets.local
|
||||
TURTLE_FAKE_UPLOAD="1"
|
||||
TURTLE_FAKE_UPLOAD_DIR="$HOME/Downloads"
|
||||
TURTLE_WORKING_DIR_PATH="$ROOT_DIR/workingdir"
|
||||
EOF
|
||||
}
|
||||
|
||||
if [ "$1" == "local" ]; then
|
||||
setup_local
|
||||
switch_local
|
||||
elif [ "$1" == "remote" ]; then
|
||||
setup_remote
|
||||
switch_remote
|
||||
else
|
||||
echo "You need to pass remote or local as argument"
|
||||
exit -1
|
||||
fi
|
||||
@@ -1,34 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ueo pipefail
|
||||
|
||||
export NODE_ENV=test
|
||||
|
||||
export ENVIRONMENT="development"
|
||||
export AWS_ACCESS_KEY_ID="aws_id"
|
||||
export AWS_SECRET_ACCESS_KEY="secret"
|
||||
export AWS_SQS_IOS_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_ios_in.fifo"
|
||||
export AWS_SQS_IOS_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_ios_in.fifo"
|
||||
export AWS_SQS_ANDROID_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_android_in.fifo"
|
||||
export AWS_SQS_ANDROID_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_android_in.fifo"
|
||||
export AWS_SQS_OUT_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_out.fifo"
|
||||
export AWS_SQS_ANDROID_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_android_in.fifo"
|
||||
export AWS_SQS_ANDROID_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_android_in_priority.fifo"
|
||||
export AWS_SQS_IOS_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_ios_in.fifo"
|
||||
export AWS_SQS_IOS_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_ios_in_priority.fifo"
|
||||
export AWS_SQS_OUT_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_out.fifo"
|
||||
export AWS_SQS_REGION="aws.west2"
|
||||
export AWS_S3_BUCKET="exp-shell-app-assets"
|
||||
export AWS_S3_REGION="us-west-1"
|
||||
export AWS_CLOUDWATCH_REGION="us-west-1"
|
||||
export API_PROTOCOL="http"
|
||||
export API_HOSTNAME="localhost"
|
||||
export API_PORT="3000"
|
||||
export API_HOSTNAME="staging.expo.io"
|
||||
export API_PORT="443"
|
||||
export REDIS_URL="noop"
|
||||
export REDIS_CONFIG_URL="noop"
|
||||
|
||||
yarn test:smoke:android
|
||||
smoke_result=$?
|
||||
|
||||
yarn test:unit
|
||||
unit_result=$?
|
||||
|
||||
[[ $smoke_result != 0 || $unit_result != 0 ]] && \
|
||||
echo && \
|
||||
echo "Tests failed." && \
|
||||
exit -1
|
||||
|
||||
exit 0
|
||||
@@ -1,13 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ueo pipefail
|
||||
|
||||
export NODE_ENV=test
|
||||
|
||||
export ENVIRONMENT="development"
|
||||
export AWS_SQS_IOS_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_ios_in.fifo"
|
||||
export AWS_SQS_ANDROID_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_android_in.fifo"
|
||||
export AWS_SQS_IOS_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_ios_in_priority.fifo"
|
||||
export AWS_SQS_ANDROID_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_android_in_priority.fifo"
|
||||
export AWS_SQS_OUT_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_out.fifo"
|
||||
export AWS_SQS_ANDROID_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_android_in.fifo"
|
||||
export AWS_SQS_ANDROID_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_android_in_priority.fifo"
|
||||
export AWS_SQS_IOS_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_ios_in.fifo"
|
||||
export AWS_SQS_IOS_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_ios_in_priority.fifo"
|
||||
export AWS_SQS_OUT_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_out.fifo"
|
||||
export AWS_SQS_REGION="aws.west2"
|
||||
export AWS_S3_BUCKET="exp-shell-app-assets"
|
||||
export AWS_S3_REGION="us-west-1"
|
||||
@@ -18,17 +20,7 @@ export API_PORT="443"
|
||||
export REDIS_URL="noop"
|
||||
export REDIS_CONFIG_URL="noop"
|
||||
|
||||
yarn init-workingdir:remote-ios
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../.. && pwd )"
|
||||
SDK_VERSION=sdk32 $ROOT_DIR/scripts/ios/fetchRemoteIosTarball.sh
|
||||
|
||||
yarn test:smoke:ios
|
||||
smoke_result=$?
|
||||
|
||||
yarn test:unit
|
||||
unit_result=$?
|
||||
|
||||
[[ $smoke_result != 0 || $unit_result != 0 ]] && \
|
||||
echo && \
|
||||
echo "Tests failed." && \
|
||||
exit -1
|
||||
|
||||
exit 0
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ueo pipefail
|
||||
|
||||
export NODE_ENV=test
|
||||
|
||||
export ENVIRONMENT="development"
|
||||
@@ -21,6 +23,3 @@ export REDIS_URL="noop"
|
||||
export REDIS_CONFIG_URL="noop"
|
||||
|
||||
yarn test:unit
|
||||
unit_result=$?
|
||||
|
||||
exit $unit_result
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
|
||||
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
|
||||
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
|
||||
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
|
||||
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
|
||||
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/android-shell-builder-5c339ff7bab6f2161d378cc70e1bf04d32ad98dd.tar.gz
|
||||
s3://exp-artifacts/android-shell-builder-89b2d24c86737d5a489c6a18467e31b2f9ce9005.tar.gz
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/android-shell-builder-cb03fdc12dc74f699a6d3f22219cbcd30e01ac46.tar.gz
|
||||
s3://exp-artifacts/android-shell-builder-0cab68670fc50ce1dc16b7d86ca7976870062c39.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
s3://exp-artifacts/ios-expo-client-15bb883c22173d5b01df19421580170e776041d0.tar.gz
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/ios-shell-builder-sdk-30-7d017a96d00fbf18ee7e5581ea47528b9cc78afd.tar.gz
|
||||
s3://exp-artifacts/ios-shell-builder-sdk-30-6863276e6b9605e31228c845a047387571959e9b.tar.gz
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/ios-shell-builder-sdk-latest-56824ecea6ba10f5a9d8991545a1c47ee3284f5f.tar.gz
|
||||
s3://exp-artifacts/ios-shell-builder-sdk-latest-e1a8bd0ab2e1eba546792c0ffc282b5b202ccc39.tar.gz
|
||||
@@ -1 +1 @@
|
||||
s3://exp-artifacts/ios-shell-builder-sdk-latest-e3855783ed0618f4dcf326dc353c6e2951e05c49.tar.gz
|
||||
s3://exp-artifacts/ios-shell-builder-sdk-latest-dc981c7110b7d508de8e9c352064dc3d745c9f72.tar.gz
|
||||
File diff suppressed because it is too large.
Load diff
Binary file not shown.
@@ -3,14 +3,13 @@ import fs from 'fs-extra';
|
||||
import nock from 'nock';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
import * as jobManager from 'turtle/jobManager';
|
||||
import logger from 'turtle/logger';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
|
||||
jest.mock('../builders/utils/uploader.ts', () => ({
|
||||
uploadBuildToS3: jest.fn(() => {
|
||||
console.log('Faking uploading build');
|
||||
logger.debug('Faking uploading build');
|
||||
return 'legit_path';
|
||||
}),
|
||||
}));
|
||||
@@ -35,7 +34,7 @@ async function performSmokeTest(recording: string) {
|
||||
});
|
||||
}
|
||||
|
||||
await jobManager.doJob();
|
||||
await jobManager.doJob(logger);
|
||||
if (!fixtureExists) {
|
||||
await fs.writeFile(recording, JSON.stringify(nock.recorder.play()));
|
||||
}
|
||||
@@ -68,11 +67,11 @@ function mockFinish() {
|
||||
.post('/', (body: any) => {
|
||||
const keys = Object.keys(body);
|
||||
if (keys.includes('MessageBody')) {
|
||||
const mb = JSON.parse(body['MessageBody']);
|
||||
const mb = JSON.parse(body.MessageBody);
|
||||
if (mb.status === 'finished') {
|
||||
return true;
|
||||
} else if (mb.status === 'errored') {
|
||||
fail('Build ended with error')
|
||||
fail('Build ended with error');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
+47
-40
@@ -1,16 +1,21 @@
|
||||
import aws from 'aws-sdk';
|
||||
import _ from 'lodash';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import calculateStatistics from 'turtle/aws/cloudwatch/statistics';
|
||||
import { EXTRA_DIMENSIONS, GROUP_HASH } from 'turtle/aws/cloudwatch/constants';
|
||||
import { Metric, MetricData, MetricConfiguration, MetricRegistrationObject, MetricsChunk } from 'turtle/aws/cloudwatch/types';
|
||||
import calculateStatistics from 'turtle/aws/cloudwatch/statistics';
|
||||
import {
|
||||
IMetric,
|
||||
IMetricConfiguration,
|
||||
IMetricData,
|
||||
IMetricsChunk,
|
||||
MetricRegistrationObject,
|
||||
} from 'turtle/aws/cloudwatch/types';
|
||||
import config from 'turtle/config';
|
||||
|
||||
interface State {
|
||||
metrics: Array<Metric>;
|
||||
interface IState {
|
||||
metrics: IMetric[];
|
||||
registeredMetrics: {
|
||||
[key: string]: MetricConfiguration;
|
||||
[key: string]: IMetricConfiguration;
|
||||
};
|
||||
intervalId: NodeJS.Timer | null;
|
||||
}
|
||||
@@ -19,7 +24,7 @@ const SUCCESS_SUFFIX = '.success';
|
||||
const FAIL_SUFFIX = '.fail';
|
||||
const CHUNK_SIZE = 20;
|
||||
|
||||
const state: State = {
|
||||
const state: IState = {
|
||||
metrics: [],
|
||||
registeredMetrics: {},
|
||||
intervalId: null,
|
||||
@@ -32,12 +37,12 @@ const cloudWatch = new aws.CloudWatch({
|
||||
secretAccessKey: config.aws.secretAccessKey,
|
||||
});
|
||||
|
||||
export function init() {
|
||||
export function init(logger: any) {
|
||||
if (config.cloudwatch.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.intervalId = setInterval(_dumpMetrics, config.cloudwatch.intervalMs);
|
||||
state.intervalId = setInterval(_dumpMetrics(logger), config.cloudwatch.intervalMs);
|
||||
}
|
||||
|
||||
export function addMetricData({
|
||||
@@ -46,7 +51,7 @@ export function addMetricData({
|
||||
success = true,
|
||||
explicitName = false,
|
||||
extraDimensions = [],
|
||||
}: MetricData) {
|
||||
}: IMetricData) {
|
||||
if (config.cloudwatch.disabled) {
|
||||
return;
|
||||
}
|
||||
@@ -57,7 +62,7 @@ export function addMetricData({
|
||||
return;
|
||||
}
|
||||
const { unit } = registeredMetric;
|
||||
const metric: Metric = {
|
||||
const metric: IMetric = {
|
||||
MetricName: metricName,
|
||||
Unit: unit,
|
||||
Value: value,
|
||||
@@ -105,26 +110,28 @@ function _calculateGroupHash(metricName: string, extraDimensions: any[]) {
|
||||
return `${metricName}${dimString}`;
|
||||
}
|
||||
|
||||
function _dumpMetrics() {
|
||||
if (!state.metrics.length && Object.keys(state.registeredMetrics).length < 0) {
|
||||
// empty metrics list, skipping
|
||||
return;
|
||||
}
|
||||
function _dumpMetrics(logger: any) {
|
||||
return () => {
|
||||
if (!state.metrics.length && Object.keys(state.registeredMetrics).length < 0) {
|
||||
// empty metrics list, skipping
|
||||
return;
|
||||
}
|
||||
|
||||
logger.trace(
|
||||
'[cloudwatch] lenghts for state.metrics=%d, state.registeredMetrics=%d',
|
||||
state.metrics.length,
|
||||
Object.keys(state.registeredMetrics).length
|
||||
);
|
||||
logger.trace(
|
||||
'[cloudwatch] lengths for state.metrics=%d, state.registeredMetrics=%d',
|
||||
state.metrics.length,
|
||||
Object.keys(state.registeredMetrics).length,
|
||||
);
|
||||
|
||||
_addEmptyMetrics(state.metrics);
|
||||
const formattedMetrics = _formatMetrics(state.metrics);
|
||||
_pushMetrics(formattedMetrics);
|
||||
_addEmptyMetrics(state.metrics);
|
||||
const formattedMetrics = _formatMetrics(state.metrics);
|
||||
_pushMetrics(formattedMetrics, logger);
|
||||
|
||||
state.metrics = [];
|
||||
state.metrics = [];
|
||||
};
|
||||
}
|
||||
|
||||
function _addEmptyMetrics(metrics: Array<Metric>) {
|
||||
function _addEmptyMetrics(metrics: IMetric[]) {
|
||||
const seenMetrics = metrics.reduce((acc, i) => {
|
||||
if (!acc.has(i.MetricName)) {
|
||||
acc.add(i.MetricName);
|
||||
@@ -134,46 +141,46 @@ function _addEmptyMetrics(metrics: Array<Metric>) {
|
||||
|
||||
const nonEmptyMetrics = _.omitBy(state.registeredMetrics, ({ addEmpty }) => !addEmpty);
|
||||
const toAdd = _.difference(Object.keys(nonEmptyMetrics), Array.from(seenMetrics));
|
||||
toAdd.forEach(name => addMetricData({ name, value: 0, explicitName: true }));
|
||||
toAdd.forEach((name) => addMetricData({ name, value: 0, explicitName: true }));
|
||||
}
|
||||
|
||||
function _formatMetrics(metrics: Array<Metric>) {
|
||||
function _formatMetrics(metrics: IMetric[]) {
|
||||
const reducedMetrics = _reduceMetrics(metrics);
|
||||
const metricsWithDimensions = reducedMetrics.map(metric => {
|
||||
const metricsWithDimensions = reducedMetrics.map((metric) => {
|
||||
const { dimensions } = state.registeredMetrics[metric.MetricName];
|
||||
const joinedDimensions = [...dimensions, ...metric[EXTRA_DIMENSIONS]];
|
||||
return joinedDimensions.map(i => Object.assign({}, metric, { Dimensions: i }));
|
||||
return joinedDimensions.map((i) => Object.assign({}, metric, { Dimensions: i }));
|
||||
});
|
||||
const metricData = _.flatten(metricsWithDimensions);
|
||||
const chunks = _.chunk(metricData, CHUNK_SIZE);
|
||||
return chunks.map(chunk => ({
|
||||
return chunks.map((chunk) => ({
|
||||
Namespace: config.cloudwatch.namespace,
|
||||
MetricData: chunk,
|
||||
}));
|
||||
}
|
||||
|
||||
function _reduceMetrics(metrics: Array<Metric>) {
|
||||
function _reduceMetrics(metrics: IMetric[]) {
|
||||
const grouped = _.groupBy(metrics, GROUP_HASH);
|
||||
return _.reduce(
|
||||
grouped,
|
||||
(acc: Array<Metric>, val: any) => {
|
||||
const { MetricName: key } = val[0] as Metric;
|
||||
(acc: IMetric[], val: any) => {
|
||||
const { MetricName: key } = val[0] as IMetric;
|
||||
const { reducer, statistics } = state.registeredMetrics[key];
|
||||
const reduced = reducer ? reducer(val) : val;
|
||||
const withStats = statistics ? calculateStatistics(reduced) : reduced;
|
||||
acc.push(...withStats);
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
function _pushMetrics(metrics: Array<MetricsChunk>) {
|
||||
function _pushMetrics(metrics: IMetricsChunk[], logger: any) {
|
||||
logger.trace('[cloudwatch] pushing metrics', JSON.stringify(metrics, null, '\t'));
|
||||
metrics.forEach(metricsChunk => {
|
||||
cloudWatch.putMetricData(metricsChunk, err => {
|
||||
metrics.forEach((metricsChunk) => {
|
||||
cloudWatch.putMetricData(metricsChunk, (err) => {
|
||||
if (err) {
|
||||
logger.warn('[cloudwatch]', err);
|
||||
logger.warn({ err }, '[cloudwatch]');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Metric } from 'turtle/aws/cloudwatch/types';
|
||||
import { IMetric } from 'turtle/aws/cloudwatch/types';
|
||||
|
||||
export function sum(metrics: Array<Metric>) {
|
||||
export function sum(metrics: IMetric[]) {
|
||||
if (metrics.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const reduced = metrics.reduce((acc: Metric | null, i: Metric) => {
|
||||
const reduced = metrics.reduce((acc: IMetric | null, i: IMetric) => {
|
||||
let result = acc;
|
||||
if (!result) {
|
||||
result = {...i};
|
||||
result = { ...i };
|
||||
} else {
|
||||
result.Value += i.Value;
|
||||
}
|
||||
@@ -16,12 +16,12 @@ export function sum(metrics: Array<Metric>) {
|
||||
return [reduced];
|
||||
}
|
||||
|
||||
export function avg(metrics: Array<Metric>) {
|
||||
export function avg(metrics: IMetric[]) {
|
||||
if (!metrics.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const metric = sum(metrics)[0] as Metric;
|
||||
const metric = sum(metrics)[0] as IMetric;
|
||||
metric.Value = metric.Value / metrics.length;
|
||||
return [metric];
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Metric, MetricWithStats } from 'turtle/aws/cloudwatch/types';
|
||||
import { IMetric, MetricWithStats } from 'turtle/aws/cloudwatch/types';
|
||||
|
||||
export default function calculateStatistics(metrics: Array<Metric>) {
|
||||
export default function calculateStatistics(metrics: IMetric[]) {
|
||||
if (!metrics.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const reduced = metrics.reduce((acc: MetricWithStats | null, i: Metric) => {
|
||||
const reduced = metrics.reduce((acc: MetricWithStats | null, i: IMetric) => {
|
||||
let result = acc;
|
||||
if (!result) {
|
||||
result = Object.assign({}, i, {
|
||||
|
||||
+12
-12
@@ -1,6 +1,6 @@
|
||||
import { EXTRA_DIMENSIONS, GROUP_HASH } from 'turtle/aws/cloudwatch/constants';
|
||||
|
||||
export interface Metric {
|
||||
export interface IMetric {
|
||||
MetricName: string;
|
||||
Unit: string;
|
||||
Value: number;
|
||||
@@ -11,8 +11,8 @@ export interface Metric {
|
||||
SampleCount: number;
|
||||
Sum: number;
|
||||
};
|
||||
[EXTRA_DIMENSIONS]: any,
|
||||
[GROUP_HASH]: string,
|
||||
[EXTRA_DIMENSIONS]: any;
|
||||
[GROUP_HASH]: string;
|
||||
}
|
||||
|
||||
export type MetricWithStats = {
|
||||
@@ -21,10 +21,10 @@ export type MetricWithStats = {
|
||||
Minimum: number;
|
||||
SampleCount: number;
|
||||
Sum: number;
|
||||
}
|
||||
} & Metric;
|
||||
},
|
||||
} & IMetric;
|
||||
|
||||
export interface MetricData {
|
||||
export interface IMetricData {
|
||||
name: string;
|
||||
value: number;
|
||||
success?: boolean;
|
||||
@@ -32,17 +32,17 @@ export interface MetricData {
|
||||
extraDimensions?: any;
|
||||
}
|
||||
|
||||
export interface MetricConfiguration {
|
||||
export interface IMetricConfiguration {
|
||||
unit: string;
|
||||
statistics?: boolean;
|
||||
reducer?: any;
|
||||
addEmpty?: boolean;
|
||||
dimensions?: any;
|
||||
};
|
||||
}
|
||||
|
||||
export type MetricRegistrationObject = { metricName: string, explicitName?: boolean } & MetricConfiguration;
|
||||
export type MetricRegistrationObject = { metricName: string, explicitName?: boolean } & IMetricConfiguration;
|
||||
|
||||
export interface MetricsChunk {
|
||||
export interface IMetricsChunk {
|
||||
Namespace: string;
|
||||
MetricData: Array<Metric>;
|
||||
};
|
||||
MetricData: IMetric[];
|
||||
}
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
import AWS from 'aws-sdk';
|
||||
import _ from 'lodash';
|
||||
import fs from 'fs-extra';
|
||||
import _ from 'lodash';
|
||||
|
||||
import config from 'turtle/config';
|
||||
|
||||
interface FileUploadParams {
|
||||
interface IFileUploadParams {
|
||||
bucketName?: string;
|
||||
key: string;
|
||||
srcPath: string;
|
||||
@@ -17,7 +17,7 @@ const s3 = new AWS.S3({
|
||||
|
||||
export default s3;
|
||||
|
||||
export async function uploadFile({ bucketName = config.s3.bucket, key, srcPath }: FileUploadParams) {
|
||||
export async function uploadFile({ bucketName = config.s3.bucket, key, srcPath }: IFileUploadParams) {
|
||||
return await s3
|
||||
.upload({
|
||||
Bucket: bucketName,
|
||||
|
||||
+11
-12
@@ -2,11 +2,10 @@ import retry from 'async-retry';
|
||||
import AWS from 'aws-sdk';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { OUTPUT_QUEUE_URL, QUEUE_URL } from 'turtle/aws/utils';
|
||||
import config from 'turtle/config';
|
||||
import { JOB_UPDATE_TYPE } from 'turtle/constants/build';
|
||||
import logger from 'turtle/logger';
|
||||
import { getCurrentJobId } from 'turtle/turtleContext';
|
||||
import { QUEUE_URL, OUTPUT_QUEUE_URL } from 'turtle/aws/utils';
|
||||
|
||||
const sqs = new AWS.SQS({
|
||||
apiVersion: '2012-11-05',
|
||||
@@ -16,7 +15,7 @@ const sqs = new AWS.SQS({
|
||||
|
||||
const VISIBILITY_TIMEOUT_SEC = 30;
|
||||
|
||||
export async function receiveMessage(priority: string) {
|
||||
export async function receiveMessage(priority: string, logger: any) {
|
||||
const params = {
|
||||
MaxNumberOfMessages: 1,
|
||||
QueueUrl: QUEUE_URL(priority),
|
||||
@@ -26,7 +25,7 @@ export async function receiveMessage(priority: string) {
|
||||
return sqs
|
||||
.receiveMessage(params)
|
||||
.promise()
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (data.Messages) {
|
||||
logger.info(`Received a message`);
|
||||
return data.Messages[0];
|
||||
@@ -35,8 +34,8 @@ export async function receiveMessage(priority: string) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error('Receive error', err);
|
||||
.catch((err) => {
|
||||
logger.error({ err }, 'Receive error');
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
@@ -62,19 +61,19 @@ export async function changeMessageVisibility(priority: string, receiptHandle: s
|
||||
}
|
||||
|
||||
// Every VISIBILITY_TIMEOUT_SEC / 3 seconds we are telling AWS SQS
|
||||
// that we're still proccessing the message, so it does not
|
||||
// that we're still processing the message, so it does not
|
||||
// send the build job to another turtle agent
|
||||
export function changeMessageVisibilityRecurring(priority: string, receiptHandle: string, jobId: string) {
|
||||
export function changeMessageVisibilityRecurring(priority: string, receiptHandle: string, jobId: string, logger: any) {
|
||||
return setInterval(() => {
|
||||
if (getCurrentJobId() === jobId) {
|
||||
changeMessageVisibility(priority, receiptHandle).catch(err => {
|
||||
logger.warn('Error at change msg visibility', err);
|
||||
changeMessageVisibility(priority, receiptHandle).catch((err) => {
|
||||
logger.warn({ err }, 'Error at change msg visibility');
|
||||
});
|
||||
}
|
||||
}, (VISIBILITY_TIMEOUT_SEC * 1000) / 3);
|
||||
}
|
||||
|
||||
export async function sendMessage(jobId: string, status: JOB_UPDATE_TYPE, data = {}) {
|
||||
export async function sendMessage(jobId: string, status: JOB_UPDATE_TYPE, data = {}, logger: any) {
|
||||
const params = {
|
||||
MessageBody: JSON.stringify({ jobId, status, ...data }),
|
||||
QueueUrl: OUTPUT_QUEUE_URL(),
|
||||
@@ -86,6 +85,6 @@ export async function sendMessage(jobId: string, status: JOB_UPDATE_TYPE, data =
|
||||
return await sqs.sendMessage(params).promise();
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`Error sending SQS message: ${JSON.stringify(params)}}`, err);
|
||||
logger.error({ err }, `Error sending SQS message: ${JSON.stringify(params)}}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export class ErrorWithCommandHelp extends Error {
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export class ErrorWithProgramHelp extends Error {
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import _ from 'lodash';
|
||||
import fs from 'fs-extra';
|
||||
import _ from 'lodash';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import builder from 'turtle/builders/android';
|
||||
import { ErrorWithCommandHelp } from 'turtle/bin/commands/errors';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
import { ErrorWithCommandHelp } from 'turtle/bin/commands/ErrorWithCommandHelp';
|
||||
import { createBuilderAction } from 'turtle/bin/utils/builder';
|
||||
import builder from 'turtle/builders/android';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
export default (program: any, setCommonCommandOptions: any) => {
|
||||
const command = program.command('build:android [project-dir]');
|
||||
@@ -14,11 +14,12 @@ export default (program: any, setCommonCommandOptions: any) => {
|
||||
.alias('ba')
|
||||
.option(
|
||||
'--keystore-path <app.jks>',
|
||||
'path to your Keystore (please provide Keystore password and Key password as EXPO_ANDROID_KEYSTORE_PASSWORD and EXPO_ANDROID_KEY_PASSWORD env variables)'
|
||||
'path to your Keystore (please provide Keystore password and Key password as EXPO_ANDROID_KEYSTORE_PASSWORD'
|
||||
+ 'and EXPO_ANDROID_KEY_PASSWORD env variables)',
|
||||
)
|
||||
.option('--keystore-alias <alias>', 'keystore Alias')
|
||||
.description(
|
||||
'Build a standalone APK for your project, signed and ready for submission to the Google Play Store.'
|
||||
'Build a standalone APK for your project, signed and ready for submission to the Google Play Store.',
|
||||
)
|
||||
.asyncAction(
|
||||
createBuilderAction({
|
||||
@@ -29,7 +30,7 @@ export default (program: any, setCommonCommandOptions: any) => {
|
||||
builder,
|
||||
platform: PLATFORMS.ANDROID,
|
||||
os: ['darwin', 'linux'],
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -57,7 +58,7 @@ const prepareCredentials = async (cmd: any) => {
|
||||
if (!credentialsExist) {
|
||||
if (someCredentialsExist) {
|
||||
throw new ErrorWithCommandHelp(
|
||||
'Please provide all required credentials - Keystore (with password), Keystore alias and Key password'
|
||||
'Please provide all required credentials - Keystore (with password), Keystore alias and Key password',
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { default as buildIOS } from 'turtle/bin/commands/build/ios';
|
||||
import { default as buildAndroid } from 'turtle/bin/commands/build/android';
|
||||
import { default as buildIOS } from 'turtle/bin/commands/build/ios';
|
||||
|
||||
function createCommand(builder: any) {
|
||||
return (program: any) => {
|
||||
return builder(program, setCommonCommandOptions);
|
||||
return (logger: any) => (program: any) => {
|
||||
return builder(program, setCommonCommandOptions, logger);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,15 +15,16 @@ function setCommonCommandOptions(cmd: any) {
|
||||
.option('-o --output <output-file-path>', 'output file path')
|
||||
.option(
|
||||
'--public-url <url>',
|
||||
'the URL of an externally hosted manifest (for self-hosted apps).'
|
||||
'the URL of an externally hosted manifest (for self-hosted apps).',
|
||||
)
|
||||
.option(
|
||||
'--release-channel <channel-name>',
|
||||
'pull from specified release channel (default: default)'
|
||||
);
|
||||
'pull from specified release channel (default: default)',
|
||||
)
|
||||
.option('-c --config <config-file>', 'specify a path to app.json');
|
||||
}
|
||||
|
||||
export default {
|
||||
ios: createCommand(buildIOS),
|
||||
android: createCommand(buildAndroid),
|
||||
}
|
||||
};
|
||||
@@ -1,11 +1,11 @@
|
||||
import fs from 'fs-extra';
|
||||
import _ from 'lodash';
|
||||
import uuid from 'uuid';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import builder from 'turtle/builders/ios';
|
||||
import { ErrorWithCommandHelp } from 'turtle/bin/commands/errors';
|
||||
import { IOS, PLATFORMS } from 'turtle/constants';
|
||||
import { ErrorWithCommandHelp } from 'turtle/bin/commands/ErrorWithCommandHelp';
|
||||
import { createBuilderAction } from 'turtle/bin/utils/builder';
|
||||
import builder from 'turtle/builders/ios';
|
||||
import { IOS, PLATFORMS } from 'turtle/constants';
|
||||
|
||||
export default (program: any, setCommonCommandOptions: any) => {
|
||||
const command = program.command('build:ios [project-dir]');
|
||||
@@ -16,16 +16,16 @@ export default (program: any, setCommonCommandOptions: any) => {
|
||||
'-t --type <build>',
|
||||
'type of build: archive|simulator',
|
||||
/^(archive|simulator)$/i,
|
||||
'archive'
|
||||
'archive',
|
||||
)
|
||||
.option('--team-id <apple-teamId>', 'Apple Team ID')
|
||||
.option(
|
||||
'--dist-p12-path <dist.p12>',
|
||||
'path to your Distribution Certificate P12 (please provide password as EXPO_IOS_DIST_P12_PASSWORD env variable)'
|
||||
'path to your Distribution Certificate P12 (please provide password as EXPO_IOS_DIST_P12_PASSWORD env variable)',
|
||||
)
|
||||
.option('--provisioning-profile-path <.mobileprovision>', 'path to your Provisioning Profile')
|
||||
.description(
|
||||
'Build a standalone IPA for your project, signed and ready for submission to the Apple App Store.'
|
||||
'Build a standalone IPA for your project, signed and ready for submission to the Apple App Store.',
|
||||
)
|
||||
.asyncAction(
|
||||
createBuilderAction({
|
||||
@@ -36,7 +36,7 @@ export default (program: any, setCommonCommandOptions: any) => {
|
||||
builder,
|
||||
platform: PLATFORMS.IOS,
|
||||
os: 'darwin',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -68,7 +68,8 @@ const prepareCredentials = async (cmd: any) => {
|
||||
|
||||
if (!credentialsExist) {
|
||||
throw new ErrorWithCommandHelp(
|
||||
'Please provide all required credentials - Apple Team ID, Distribution Certificate P12 (with password) and Provisioning Profile'
|
||||
'Please provide all required credentials'
|
||||
+ '- Apple Team ID, Distribution Certificate P12 (with password) and Provisioning Profile',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export class ErrorWithProgramHelp extends Error {}
|
||||
export class ErrorWithCommandHelp extends Error {}
|
||||
@@ -1,8 +1,9 @@
|
||||
import builders from 'turtle/bin/commands/build';
|
||||
import setup from 'turtle/bin/commands/setup';
|
||||
|
||||
export const setupIOS = setup.ios;
|
||||
export const setupAndroid = setup.android;
|
||||
|
||||
export const buildIOS = builders.ios;
|
||||
export const buildAndroid = builders.android;
|
||||
export default {
|
||||
setupIOS: setup.ios,
|
||||
setupAndroid: setup.android,
|
||||
buildIOS: builders.ios,
|
||||
buildAndroid: builders.android,
|
||||
};
|
||||
@@ -1,12 +1,10 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import logger from 'turtle/logger';
|
||||
import { ErrorWithCommandHelp } from 'turtle/bin/commands/ErrorWithCommandHelp';
|
||||
import { ErrorWithProgramHelp } from 'turtle/bin/commands/ErrorWithProgramHelp';
|
||||
import setup from 'turtle/bin/setup/setup';
|
||||
import { ErrorWithCommandHelp, ErrorWithProgramHelp } from 'turtle/bin/commands/errors';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
const l = logger.withFields({ buildPhase: 'setting up environment' });
|
||||
|
||||
function createSetupCommand(platform: string, os?: string) {
|
||||
return (program: any) => {
|
||||
const command = program.command(`setup:${platform}`);
|
||||
@@ -16,24 +14,25 @@ function createSetupCommand(platform: string, os?: string) {
|
||||
.description(`Setup environment for building ${_.capitalize(platform)} standalone apps.`)
|
||||
.option(
|
||||
'--sdk-version <sdk-version>',
|
||||
'version of Expo SDK shell app to download (optional)'
|
||||
'version of Expo SDK shell app to download (optional)',
|
||||
)
|
||||
.asyncAction(async (cmd: any) => await setupAction(program, cmd, platform, os));
|
||||
};
|
||||
}
|
||||
|
||||
async function setupAction(program: any, cmd: any, platform: string, os?: string) {
|
||||
async function setupAction(program: any, cmd: any, platform: string, logger: any, os?: string) {
|
||||
try {
|
||||
if (os && process.platform !== os) {
|
||||
throw new Error('We don\'t support running standalone app builds for this platform on your operating system');
|
||||
}
|
||||
|
||||
const { sdkVersion } = cmd;
|
||||
await setup(platform, sdkVersion);
|
||||
l.info('it\'s all set!')
|
||||
const l = logger.child({ buildPhase: 'setting up environment' });
|
||||
|
||||
await setup(platform, l, sdkVersion);
|
||||
l.info('it\'s all set!');
|
||||
} catch (err) {
|
||||
logger.error(`Failed to setup environment for ${platform} builds`);
|
||||
logger.error(err.stack);
|
||||
logger.error({ err }, `Failed to setup environment for ${platform} builds`);
|
||||
if (err instanceof ErrorWithCommandHelp) {
|
||||
cmd.help();
|
||||
} else if (err instanceof ErrorWithProgramHelp) {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -16,8 +16,8 @@ const resolveEnv = () => {
|
||||
};
|
||||
|
||||
const currentEnv = resolveEnv();
|
||||
const isLocal = (env: string) => env === 'local';
|
||||
const isStaging = (env: string) => env === 'staging';
|
||||
const isLocal = (envName: string) => envName === 'local';
|
||||
const isStaging = (envName: string) => envName === 'staging';
|
||||
|
||||
const apiConfig = {
|
||||
protocol: 'https',
|
||||
|
||||
+19
-15
@@ -1,11 +1,13 @@
|
||||
import { LoggerDetach, ModuleVersion } from 'xdl';
|
||||
import path from 'path';
|
||||
|
||||
import program, { Command } from 'commander';
|
||||
import _ from 'lodash';
|
||||
import fs from 'fs-extra';
|
||||
import { LoggerDetach, ModuleVersion } from 'xdl';
|
||||
|
||||
import commands from 'turtle/bin/commands';
|
||||
import logger from 'turtle/logger';
|
||||
import * as commands from 'turtle/bin/commands';
|
||||
|
||||
const { name, version } = require('../../package.json');
|
||||
const { name, version } = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'));
|
||||
|
||||
const ModuleVersionChecker = ModuleVersion.createModuleVersionChecker(name, version);
|
||||
|
||||
@@ -15,36 +17,38 @@ Command.prototype.asyncAction = function asyncAction(asyncFn: (...asyncFnArgs: a
|
||||
return this.action(async (...args) => {
|
||||
try {
|
||||
await checkForUpdateAsync();
|
||||
} catch (e) {}
|
||||
} catch (err) {
|
||||
logger.warn({ err }, 'Failed to check for turtle-cli update.');
|
||||
}
|
||||
return await asyncFn(...args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function run(programName: string) {
|
||||
runAsync(programName).catch(e => {
|
||||
logger.error('Uncaught Error', e);
|
||||
runAsync(programName).catch((err) => {
|
||||
logger.error({ err }, 'Uncaught Error');
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
async function runAsync(programName: string) {
|
||||
program.version(version);
|
||||
Object.values(commands).forEach(command => registerCommand(program, command));
|
||||
Object.values(commands).map((command) => command(logger)).forEach((command) => registerCommand(program, command));
|
||||
program.parse(process.argv);
|
||||
|
||||
const subCommand = process.argv[2];
|
||||
if (subCommand) {
|
||||
const commands = program.commands.reduce((acc: Array<string>, command: any) => {
|
||||
acc.push(command['_name']);
|
||||
const alias = command['_alias'];
|
||||
const commandNames = program.commands.reduce((acc: string[], command: any) => {
|
||||
acc.push(command._name);
|
||||
const alias = command._alias;
|
||||
if (alias) {
|
||||
acc.push(alias);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
if (!_.includes(commands, subCommand)) {
|
||||
if (!commandNames.includes(subCommand)) {
|
||||
logger.error(
|
||||
`"${subCommand}" is not an ${programName} command. See "${programName} --help" for the full list of commands.`
|
||||
`"${subCommand}" is not an ${programName} command. See "${programName} --help" for the full list of commands.`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -60,7 +64,7 @@ async function checkForUpdateAsync() {
|
||||
logger.warn(
|
||||
`There is a new version of ${name} available (${latest}).
|
||||
You are currently using ${name} ${current}
|
||||
Run \`npm install -g ${name}\` to get the latest version`
|
||||
Run \`npm install -g ${name}\` to get the latest version`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,19 @@ import path from 'path';
|
||||
import util from 'util';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { ExponentTools } from 'xdl';
|
||||
import _which from 'which';
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import logger from 'turtle/logger';
|
||||
import config from 'turtle/config';
|
||||
import { IToolDefinition } from 'turtle/bin/setup/utils/toolsDetector';
|
||||
import { checkSystem, ensureShellAppIsPresent } from 'turtle/bin/setup/utils/common';
|
||||
import { formatShellAppDirectory } from 'turtle/builders/utils/android/workingdir';
|
||||
import ensureAndroidSDKIsPresent from 'turtle/bin/setup/android/sdk';
|
||||
import ensureAndroidNDKIsPresent from 'turtle/bin/setup/android/ndk';
|
||||
import ensureAndroidSDKIsPresent from 'turtle/bin/setup/android/sdk';
|
||||
import { checkSystem, ensureShellAppIsPresent } from 'turtle/bin/setup/utils/common';
|
||||
import { IToolDefinition } from 'turtle/bin/setup/utils/toolsDetector';
|
||||
import { formatShellAppDirectory } from 'turtle/builders/utils/android/workingdir';
|
||||
import config from 'turtle/config';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
const which = util.promisify(_which);
|
||||
const REQUIRED_TOOLS: Array<IToolDefinition> = [
|
||||
const REQUIRED_TOOLS: IToolDefinition[] = [
|
||||
{
|
||||
command: 'bash',
|
||||
missingDescription: 'Please install bash',
|
||||
@@ -31,34 +30,33 @@ const REQUIRED_TOOLS: Array<IToolDefinition> = [
|
||||
const { status, stdout } = await ExponentTools.spawnAsyncThrowError(
|
||||
'javac',
|
||||
['-version'],
|
||||
{ stdio: 'pipe' }
|
||||
{ stdio: 'pipe' },
|
||||
);
|
||||
if (stdout.match(/no java runtime present/i)) {
|
||||
return false;
|
||||
}
|
||||
return status === 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
|
||||
const l = logger.withFields(LOGGER_FIELDS);
|
||||
|
||||
export default async function setup(sdkVersion?: string) {
|
||||
await checkSystem(REQUIRED_TOOLS);
|
||||
await prepareAndroidEnv();
|
||||
export default async function setup(logger: any, sdkVersion?: string) {
|
||||
await checkSystem(REQUIRED_TOOLS, logger);
|
||||
await prepareAndroidEnv(logger);
|
||||
if (sdkVersion) {
|
||||
await ensureShellAppIsPresent(
|
||||
sdkVersion,
|
||||
{ formatShellAppDirectory, formatShellAppTarballUriPath },
|
||||
_shellAppPostDownloadAction
|
||||
logger,
|
||||
_shellAppPostDownloadAction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function prepareAndroidEnv() {
|
||||
async function prepareAndroidEnv(logger: any) {
|
||||
await fs.ensureDir(config.directories.androidDependenciesDir);
|
||||
const sdkConfig = await ensureAndroidSDKIsPresent();
|
||||
const ndkConfig = await ensureAndroidNDKIsPresent();
|
||||
const sdkConfig = await ensureAndroidSDKIsPresent(logger);
|
||||
const ndkConfig = await ensureAndroidNDKIsPresent(logger);
|
||||
_setEnvVars({ ...sdkConfig.envVars, ...ndkConfig.envVars });
|
||||
_alterPath([...sdkConfig.path, ...sdkConfig.path]);
|
||||
}
|
||||
@@ -67,21 +65,23 @@ function formatShellAppTarballUriPath(sdkMajorVersion: string) {
|
||||
return path.join(config.directories.shellTarballsDir, PLATFORMS.ANDROID, `sdk${sdkMajorVersion}`);
|
||||
}
|
||||
|
||||
async function _shellAppPostDownloadAction(workingdir: string) {
|
||||
async function _shellAppPostDownloadAction(workingdir: string, logger: any) {
|
||||
const inWorkingdir = (filename: string) => path.join(workingdir, filename);
|
||||
|
||||
await fs.move(inWorkingdir('package.json'), inWorkingdir('exponent-package.json'));
|
||||
await fs.move(inWorkingdir('universe-package.json'), inWorkingdir('package.json'));
|
||||
await _installNodeModules(workingdir);
|
||||
await _installNodeModules(workingdir, logger);
|
||||
await fs.move(inWorkingdir('package.json'), inWorkingdir('universe-package.json'));
|
||||
await fs.move(inWorkingdir('exponent-package.json'), inWorkingdir('package.json'));
|
||||
|
||||
// TODO: remove following lines after upgrading android shell app
|
||||
const toolsPublicDir = path.join(workingdir, 'tools-public');
|
||||
await _installNodeModules(toolsPublicDir);
|
||||
await _installNodeModules(toolsPublicDir, logger);
|
||||
}
|
||||
|
||||
async function _installNodeModules(cwd: string) {
|
||||
async function _installNodeModules(cwd: string, logger: any) {
|
||||
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
|
||||
const l = logger.child(LOGGER_FIELDS);
|
||||
l.info(`installing dependencies in ${cwd} directory...`);
|
||||
const command = await _shouldUseYarnOrNpm();
|
||||
await ExponentTools.spawnAsyncThrowError(command, ['install'], {
|
||||
@@ -107,7 +107,7 @@ function _setEnvVars(envVars: object) {
|
||||
.forEach(([key, value]) => process.env[key] = value);
|
||||
}
|
||||
|
||||
function _alterPath(newPaths: Array<string>) {
|
||||
function _alterPath(newPaths: string[]) {
|
||||
const currentPaths = process.env.PATH ? process.env.PATH.split(':') : [];
|
||||
const paths = [...newPaths, ...currentPaths];
|
||||
process.env.PATH = paths.join(':');
|
||||
|
||||
@@ -4,27 +4,24 @@ import path from 'path';
|
||||
import decompress from 'decompress';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import download from 'turtle/bin/setup/utils/downloader';
|
||||
import { formatArtifactDownloadPath } from 'turtle/bin/setup/utils/common';
|
||||
import download from 'turtle/bin/setup/utils/downloader';
|
||||
import config from 'turtle/config';
|
||||
|
||||
const ANDROID_NDK_URL = `https://dl.google.com/android/repository/android-ndk-r10e-${os.platform()}-x86_64.zip`;
|
||||
|
||||
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
|
||||
const l = logger.withFields(LOGGER_FIELDS);
|
||||
|
||||
export default async function ensureAndroidNDKIsPresent() {
|
||||
export default async function ensureAndroidNDKIsPresent(logger: any) {
|
||||
const androidNdkDir = path.join(config.directories.androidDependenciesDir, 'ndk');
|
||||
if (!(await fs.pathExists(androidNdkDir))) {
|
||||
await fs.ensureDir(androidNdkDir);
|
||||
const androidNdkDownloadPath = formatArtifactDownloadPath(ANDROID_NDK_URL);
|
||||
|
||||
try {
|
||||
const l = logger.child({ buildPhase: 'setting up environment' });
|
||||
l.info('Downloading Android NDK');
|
||||
await fs.ensureDir(config.directories.artifactsDir);
|
||||
await download(ANDROID_NDK_URL, androidNdkDownloadPath);
|
||||
l.info('Decompressing Android NDK')
|
||||
l.info('Decompressing Android NDK');
|
||||
await decompress(androidNdkDownloadPath, androidNdkDir, { strip: 1 });
|
||||
l.info('Android NDK installed successfully');
|
||||
} catch (err) {
|
||||
@@ -47,5 +44,5 @@ function _createEnvVars(androidNdkDir: string) {
|
||||
}
|
||||
|
||||
function _createPaths(androidNdkDir: string) {
|
||||
return [androidNdkDir]
|
||||
return [androidNdkDir];
|
||||
}
|
||||
@@ -5,28 +5,25 @@ import decompress from 'decompress';
|
||||
import fs from 'fs-extra';
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import download from 'turtle/bin/setup/utils/downloader';
|
||||
import { formatArtifactDownloadPath } from 'turtle/bin/setup/utils/common';
|
||||
import download from 'turtle/bin/setup/utils/downloader';
|
||||
import config from 'turtle/config';
|
||||
|
||||
const ANDROID_SDK_URL = 'https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip';
|
||||
|
||||
const ANDROID_SDK_URL = 'https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip';
|
||||
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
|
||||
const l = logger.withFields(LOGGER_FIELDS);
|
||||
|
||||
export default async function ensureAndroidSDKIsPresent() {
|
||||
export default async function ensureAndroidSDKIsPresent(logger: any) {
|
||||
const androidSdkDir = path.join(config.directories.androidDependenciesDir, 'sdk');
|
||||
if (!(await fs.pathExists(androidSdkDir))) {
|
||||
await fs.ensureDir(androidSdkDir);
|
||||
const androidSdkDownloadPath = formatArtifactDownloadPath(ANDROID_SDK_URL);
|
||||
|
||||
try {
|
||||
const l = logger.child(LOGGER_FIELDS);
|
||||
l.info('Downloading Android SDK');
|
||||
await fs.ensureDir(config.directories.artifactsDir);
|
||||
await download(ANDROID_SDK_URL, androidSdkDownloadPath);
|
||||
await fs.ensureDir(androidSdkDir);
|
||||
l.info('Decompressing Android SDK')
|
||||
l.info('Decompressing Android SDK');
|
||||
await decompress(androidSdkDownloadPath, androidSdkDir);
|
||||
await fs.remove(androidSdkDownloadPath);
|
||||
l.info('Configuring Android SDK, this may take a while');
|
||||
@@ -73,6 +70,6 @@ function _createPaths(androidSdkDir: string) {
|
||||
path.join(androidSdkDir, 'platform-tools'),
|
||||
path.join(androidSdkDir, 'tools'),
|
||||
path.join(androidSdkDir, 'tools/bin'),
|
||||
path.join(androidSdkDir, 'build-tools/25.0.0'),
|
||||
path.join(androidSdkDir, 'build-tools/28.0.3'),
|
||||
];
|
||||
}
|
||||
@@ -2,13 +2,13 @@ import path from 'path';
|
||||
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import { checkSystem, ensureShellAppIsPresent } from 'turtle/bin/setup/utils/common';
|
||||
import { IToolDefinition } from 'turtle/bin/setup/utils/toolsDetector';
|
||||
import { formatShellAppDirectory } from 'turtle/builders/utils/ios/workingdir';
|
||||
import config from 'turtle/config';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
const REQUIRED_TOOLS: Array<IToolDefinition> = [
|
||||
const REQUIRED_TOOLS: IToolDefinition[] = [
|
||||
{
|
||||
command: 'bash',
|
||||
missingDescription: 'Please install bash',
|
||||
@@ -24,23 +24,23 @@ const REQUIRED_TOOLS: Array<IToolDefinition> = [
|
||||
const { status, stdout } = await ExponentTools.spawnAsyncThrowError(
|
||||
'xcodebuild',
|
||||
['-version'],
|
||||
{ stdio: 'pipe' }
|
||||
{ stdio: 'pipe' },
|
||||
);
|
||||
if (stdout.match(/requires xcode/i)) {
|
||||
return false;
|
||||
}
|
||||
return status === 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default async function setup(sdkVersion?: string) {
|
||||
await checkSystem(REQUIRED_TOOLS);
|
||||
export default async function setup(logger: any, sdkVersion?: string) {
|
||||
await checkSystem(REQUIRED_TOOLS, logger);
|
||||
if (sdkVersion) {
|
||||
await ensureShellAppIsPresent(sdkVersion, {
|
||||
formatShellAppDirectory,
|
||||
formatShellAppTarballUriPath,
|
||||
});
|
||||
}, logger);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import isSemver from 'is-semver';
|
||||
|
||||
import setupIos from 'turtle/bin/setup/ios';
|
||||
import setupAndroid from 'turtle/bin/setup/android';
|
||||
import setupIos from 'turtle/bin/setup/ios';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
export default async function setup(platform: string, sdkVersion?: string) {
|
||||
export default async function setup(platform: string, logger: any, sdkVersion?: string) {
|
||||
if (sdkVersion && !isSemver(sdkVersion)) {
|
||||
throw new Error('SDK version is not valid.');
|
||||
}
|
||||
|
||||
if (platform === PLATFORMS.IOS) {
|
||||
return await setupIos(sdkVersion);
|
||||
return await setupIos(logger, sdkVersion);
|
||||
} else if (platform === PLATFORMS.ANDROID) {
|
||||
return await setupAndroid(sdkVersion);
|
||||
return await setupAndroid(logger, sdkVersion);
|
||||
} else {
|
||||
throw new Error('This should never happen :(');
|
||||
}
|
||||
|
||||
@@ -1,56 +1,63 @@
|
||||
import path from 'path';
|
||||
|
||||
import { ExponentTools } from 'xdl';
|
||||
import fs from 'fs-extra';
|
||||
import tar from 'tar';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import { IToolDefinition, ensureToolsAreInstalled } from 'turtle/bin/setup/utils/toolsDetector';
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import download from 'turtle/bin/setup/utils/downloader';
|
||||
|
||||
const l = logger.withFields({ buildPhase: 'setting up environment' });
|
||||
import { ensureToolsAreInstalled, IToolDefinition } from 'turtle/bin/setup/utils/toolsDetector';
|
||||
import { IShellAppDirectoryConfig } from 'turtle/builders/utils/workingdir';
|
||||
import config from 'turtle/config';
|
||||
|
||||
interface IShellAppFormaters {
|
||||
formatShellAppDirectory: (sdkVersion: string) => string;
|
||||
formatShellAppDirectory: (config: IShellAppDirectoryConfig) => string;
|
||||
formatShellAppTarballUriPath: (sdkMajorVersion: string) => string;
|
||||
}
|
||||
|
||||
type PostDownloadAction = (workingdir: string) => void;
|
||||
type PostDownloadAction = (workingdir: string, logger: any) => void;
|
||||
|
||||
export async function checkSystem(requiredTools: Array<IToolDefinition>) {
|
||||
await ensureToolsAreInstalled(requiredTools);
|
||||
export async function checkSystem(requiredTools: IToolDefinition[], logger: any) {
|
||||
await ensureToolsAreInstalled(requiredTools, logger);
|
||||
}
|
||||
|
||||
export async function ensureShellAppIsPresent(sdkVersion: string, formatters: IShellAppFormaters, postDownloadAction?: PostDownloadAction) {
|
||||
const workingdir = formatters.formatShellAppDirectory(sdkVersion);
|
||||
export async function ensureShellAppIsPresent(
|
||||
sdkVersion: string,
|
||||
formatters: IShellAppFormaters,
|
||||
logger: any,
|
||||
postDownloadAction?: PostDownloadAction,
|
||||
) {
|
||||
const workingdir = formatters.formatShellAppDirectory({ sdkVersion });
|
||||
const l = logger.child({ buildPhase: 'setting up environment' });
|
||||
if (await fs.pathExists(workingdir)) {
|
||||
return;
|
||||
}
|
||||
l.info(`shell app for SDK ${sdkVersion} doesn't exist, downloading...`);
|
||||
await _downloadShellApp(sdkVersion, workingdir, formatters);
|
||||
await _downloadShellApp(sdkVersion, workingdir, formatters, l);
|
||||
if (postDownloadAction) {
|
||||
await postDownloadAction(workingdir);
|
||||
await postDownloadAction(workingdir, logger);
|
||||
}
|
||||
}
|
||||
|
||||
async function _downloadShellApp(sdkVersion: string, targetDirectory: string, formatters: IShellAppFormaters) {
|
||||
async function _downloadShellApp(
|
||||
sdkVersion: string,
|
||||
targetDirectory: string,
|
||||
formatters: IShellAppFormaters,
|
||||
logger: any,
|
||||
) {
|
||||
const shellAppTarballS3Uri = await _readShellAppTarballS3Uri(sdkVersion, formatters);
|
||||
const tarballDownloadTargetPath = formatArtifactDownloadPath(shellAppTarballS3Uri);
|
||||
await fs.ensureDir(config.directories.artifactsDir);
|
||||
await download(shellAppTarballS3Uri, tarballDownloadTargetPath);
|
||||
l.info('shell app has been downloaded');
|
||||
logger.info('shell app has been downloaded');
|
||||
await fs.ensureDir(targetDirectory);
|
||||
l.info('extracting shell app (this may take a while)...');
|
||||
logger.info('extracting shell app (this may take a while)...');
|
||||
await tar.x({
|
||||
file: tarballDownloadTargetPath,
|
||||
C: targetDirectory
|
||||
C: targetDirectory,
|
||||
});
|
||||
l.info('shell app extracted');
|
||||
logger.info('shell app extracted');
|
||||
}
|
||||
|
||||
|
||||
export function formatArtifactDownloadPath(uri: string) {
|
||||
const { base } = path.parse(uri);
|
||||
return path.join(config.directories.artifactsDir, base);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { URL } from 'url';
|
||||
import fs from 'fs';
|
||||
import { URL } from 'url';
|
||||
|
||||
import ProgressBar from 'progress';
|
||||
import request from 'request';
|
||||
import requestProgress from 'request-progress';
|
||||
import ProgressBar from 'progress';
|
||||
|
||||
const S3_PROTOCOL = 's3:';
|
||||
const PROGRESS_BAR_CONFIG = {
|
||||
@@ -38,7 +38,6 @@ export default async function download(url: string, destPath: string) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function convertS3ToHttpsUrl(s3UrlRaw: string) {
|
||||
const { host: s3Bucket, pathname: s3Path, protocol } = new URL(s3UrlRaw);
|
||||
if (protocol !== S3_PROTOCOL) {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import util from 'util';
|
||||
import _which from 'which';
|
||||
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
const which = util.promisify(_which);
|
||||
const l = logger.withFields({ buildPhase: 'setting up environment' });
|
||||
|
||||
export interface IToolDefinition {
|
||||
command: string;
|
||||
@@ -12,7 +9,7 @@ export interface IToolDefinition {
|
||||
testFn?: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
export async function ensureToolsAreInstalled(tools: Array<IToolDefinition>) {
|
||||
export async function ensureToolsAreInstalled(tools: IToolDefinition[], logger: any) {
|
||||
let isAnyToolMissing = false;
|
||||
for (const { command, missingDescription, testFn } of tools) {
|
||||
try {
|
||||
@@ -24,11 +21,12 @@ export async function ensureToolsAreInstalled(tools: Array<IToolDefinition>) {
|
||||
await which(command);
|
||||
}
|
||||
} catch (err) {
|
||||
const l = logger.child({ buildPhase: 'setting up environment' });
|
||||
isAnyToolMissing = true;
|
||||
if (!testFn) {
|
||||
l.error(`${command} is missing in your $PATH`);
|
||||
l.error({ err }, `${command} is missing in your $PATH`);
|
||||
}
|
||||
l.error(missingDescription);
|
||||
l.error({ err }, missingDescription);
|
||||
}
|
||||
}
|
||||
if (isAnyToolMissing) {
|
||||
|
||||
+15
-12
@@ -1,13 +1,14 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import logger from 'turtle/logger';
|
||||
import { sanitizeJob } from 'turtle/validator';
|
||||
import * as UserUtils from 'turtle/bin/utils/user';
|
||||
import * as ProjectUtils from 'turtle/bin/utils/project';
|
||||
import { ErrorWithCommandHelp, ErrorWithProgramHelp } from 'turtle/bin/commands/errors';
|
||||
import { ErrorWithCommandHelp } from 'turtle/bin/commands/ErrorWithCommandHelp';
|
||||
import { ErrorWithProgramHelp } from 'turtle/bin/commands/ErrorWithProgramHelp';
|
||||
import setup from 'turtle/bin/setup/setup';
|
||||
import * as ProjectUtils from 'turtle/bin/utils/project';
|
||||
import * as UserUtils from 'turtle/bin/utils/user';
|
||||
import { sanitizeJob } from 'turtle/validator';
|
||||
|
||||
export function createBuilderAction({
|
||||
logger,
|
||||
program,
|
||||
command,
|
||||
prepareCredentials,
|
||||
@@ -29,7 +30,9 @@ export function createBuilderAction({
|
||||
}
|
||||
|
||||
if (cmd.buildDir && cmd.output) {
|
||||
throw new ErrorWithCommandHelp('You can\'t provide both --build-dir and --output parameters, please choose one');
|
||||
throw new ErrorWithCommandHelp(
|
||||
'You can\'t provide both --build-dir and --output parameters, please choose one',
|
||||
);
|
||||
}
|
||||
|
||||
const userData = {
|
||||
@@ -44,10 +47,11 @@ export function createBuilderAction({
|
||||
throw new ErrorWithCommandHelp('You must provide both Expo username and password');
|
||||
}
|
||||
} else if (!cmd.publicUrl) {
|
||||
throw new ErrorWithCommandHelp('You must provide your Expo username and password unless you specify --public-url to your project manifest.');
|
||||
throw new ErrorWithCommandHelp(
|
||||
'You must provide your Expo username and password unless you specify --public-url to your project manifest.',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const args = {
|
||||
releaseChannel: cmd.releaseChannel || 'default',
|
||||
buildType: cmd.type,
|
||||
@@ -56,9 +60,9 @@ export function createBuilderAction({
|
||||
publicUrl: cmd.publicUrl,
|
||||
};
|
||||
|
||||
const appJSON = await ProjectUtils.loadAppJSON(projectDirArg);
|
||||
const appJSON = await ProjectUtils.loadAppJSON(projectDirArg, cmd.config);
|
||||
const sdkVersion = _.get(appJSON, 'expo.sdkVersion');
|
||||
await setup(platform, sdkVersion);
|
||||
await setup(platform, logger, sdkVersion);
|
||||
const credentials = await prepareCredentials(cmd);
|
||||
const rawJob = {
|
||||
...buildJobObject(appJSON, args, credentials),
|
||||
@@ -68,8 +72,7 @@ export function createBuilderAction({
|
||||
const job = await sanitizeJob(rawJob);
|
||||
await builder(job);
|
||||
} catch (err) {
|
||||
logger.error(`Failed to build standalone app: ${err.message}`);
|
||||
logger.error(err.stack);
|
||||
logger.error({ err }, 'Failed to build standalone app');
|
||||
if (err instanceof ErrorWithCommandHelp) {
|
||||
command.help();
|
||||
} else if (err instanceof ErrorWithProgramHelp) {
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
export async function loadAppJSON(projectDirArg: string) {
|
||||
const projectDir = resolveAbsoluteDir(projectDirArg);
|
||||
const appJSONPath = path.join(projectDir, 'app.json');
|
||||
export async function loadAppJSON(projectDirArg: string, config: string | null) {
|
||||
let appJSONPath;
|
||||
if (config) {
|
||||
appJSONPath = resolveAbsoluteDir(config);
|
||||
} else {
|
||||
const projectDir = resolveAbsoluteDir(projectDirArg);
|
||||
appJSONPath = path.join(projectDir, 'app.json');
|
||||
}
|
||||
const appJSONExists = await fs.pathExists(appJSONPath);
|
||||
if (!appJSONExists) {
|
||||
throw new Error(`Couldn't find app.json in ${projectDir} directory.`);
|
||||
throw new Error(`Couldn't find app.json.`);
|
||||
} else {
|
||||
return require(appJSONPath);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { User as UserManager } from 'xdl';
|
||||
|
||||
import { ErrorWithProgramHelp } from 'turtle/bin/commands/errors';
|
||||
import { ErrorWithProgramHelp } from 'turtle/bin/commands/ErrorWithProgramHelp';
|
||||
|
||||
interface IUserData {
|
||||
username?: string;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
class BuildError extends Error {
|
||||
public reason: BuildErrorReason;
|
||||
|
||||
constructor(reason: BuildErrorReason, message: string) {
|
||||
super(message);
|
||||
this.name = 'BuildError';
|
||||
this.reason = reason;
|
||||
}
|
||||
}
|
||||
|
||||
enum BuildErrorReason {
|
||||
SESSION_EXPIRED = 'session-expired',
|
||||
PROVISIONING_PROFILE_MISSING = 'provisioning-profile-missing',
|
||||
}
|
||||
|
||||
export default BuildError;
|
||||
export { BuildErrorReason };
|
||||
+20
-13
@@ -2,30 +2,35 @@ import path from 'path';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import _ from 'lodash';
|
||||
import { AndroidShellApp, ImageUtils } from 'xdl';
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import { AndroidShellApp, ImageUtils } from 'xdl';
|
||||
|
||||
import getOrCreateCredentials from 'turtle/builders/utils/android/credentials';
|
||||
import { formatShellAppDirectory } from 'turtle/builders/utils/android/workingdir';
|
||||
import * as commonUtils from 'turtle/builders/utils/common';
|
||||
import * as imageHelpers from 'turtle/builders/utils/image';
|
||||
import { uploadBuildToS3 } from 'turtle/builders/utils/uploader';
|
||||
import { ensureCanBuildSdkVersion } from 'turtle/builders/utils/version';
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import { IAndroidCredentials, IJob, IJobResult } from 'turtle/job';
|
||||
import { formatShellAppDirectory } from 'turtle/builders/utils/android/workingdir';
|
||||
|
||||
export default async function buildAndroid(jobData: IJob): Promise<IJobResult> {
|
||||
const credentials = await getOrCreateCredentials(jobData);
|
||||
const apkFilePath = await runShellAppBuilder(jobData, credentials);
|
||||
export default async function buildAndroid(jobData: IJob, logger: any): Promise<IJobResult> {
|
||||
await ensureCanBuildSdkVersion(jobData);
|
||||
const credentials = await getOrCreateCredentials(jobData, logger);
|
||||
const apkFilePath = await runShellAppBuilder(jobData, credentials, logger);
|
||||
const randomHex = uuidv4().replace(/-/g, '');
|
||||
const s3Filename = `${jobData.experienceName}-${randomHex}-signed.apk`;
|
||||
const s3FileKey = `android/${s3Filename}`;
|
||||
const fakeUploadFilename = s3Filename.replace('/', '\\');
|
||||
const artifactUrl = await uploadBuildToS3({
|
||||
logger,
|
||||
uploadPath: apkFilePath,
|
||||
s3FileKey,
|
||||
...config.builder.fakeUpload && {
|
||||
fakeUploadBuildPath: jobData.fakeUploadBuildPath ? jobData.fakeUploadBuildPath : path.join(jobData.fakeUploadDir || config.directories.fakeUploadDir, fakeUploadFilename)
|
||||
fakeUploadBuildPath:
|
||||
jobData.fakeUploadBuildPath
|
||||
? jobData.fakeUploadBuildPath
|
||||
: path.join(jobData.fakeUploadDir || config.directories.fakeUploadDir, fakeUploadFilename),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -35,6 +40,7 @@ export default async function buildAndroid(jobData: IJob): Promise<IJobResult> {
|
||||
async function runShellAppBuilder(
|
||||
jobData: IJob,
|
||||
credentials: IAndroidCredentials,
|
||||
logger: any,
|
||||
): Promise<string> {
|
||||
const { temporaryFilesRoot } = config.directories;
|
||||
await fs.ensureDir(temporaryFilesRoot);
|
||||
@@ -57,25 +63,26 @@ async function runShellAppBuilder(
|
||||
|
||||
const outputFilePath = path.join(temporaryFilesRoot, `shell-signed-${jobData.id}.apk`);
|
||||
|
||||
const { manifest: { sdkVersion: sdkVersionFromManifest = null } = {}, sdkVersion: sdkVersionFromJob } = jobData;
|
||||
const sdkVersion = sdkVersionFromJob || sdkVersionFromManifest;
|
||||
const workingDir = formatShellAppDirectory(sdkVersion);
|
||||
const { config: jobConfig, manifest, sdkVersion: sdkVersionFromJob } = jobData;
|
||||
const sdkVersion = _.get(manifest, 'sdkVersion', sdkVersionFromJob);
|
||||
const workingDir = formatShellAppDirectory({ sdkVersion });
|
||||
|
||||
try {
|
||||
await AndroidShellApp.createAndroidShellAppAsync({
|
||||
url: commonUtils.getExperienceUrl(jobData),
|
||||
sdkVersion: _.get(jobData, 'manifest.sdkVersion') || jobData.sdkVersion,
|
||||
sdkVersion,
|
||||
keystore: tempKeystorePath,
|
||||
manifest,
|
||||
alias: credentials.keystoreAlias,
|
||||
keystorePassword: credentials.keystorePassword,
|
||||
keyPassword: credentials.keyPassword,
|
||||
privateConfigFile: tempShellAppConfigPath,
|
||||
releaseChannel: jobData.config.releaseChannel,
|
||||
releaseChannel: jobConfig.releaseChannel,
|
||||
workingDir,
|
||||
outputFile: outputFilePath,
|
||||
});
|
||||
} catch (err) {
|
||||
commonUtils.logErrorOnce(err);
|
||||
commonUtils.logErrorOnce(err, logger);
|
||||
throw err;
|
||||
} finally {
|
||||
if (!config.builder.skipCleanup) {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import buildIOS from './ios';
|
||||
import { IJob, IJobResult } from 'turtle/job';
|
||||
import buildAndroid from './android';
|
||||
import buildIOS from './ios';
|
||||
|
||||
export default {
|
||||
const builders: { [index: string]: (job: IJob, logger: any) => Promise<IJobResult>} = {
|
||||
ios: buildIOS,
|
||||
android: buildAndroid,
|
||||
};
|
||||
|
||||
export default builders;
|
||||
+12
-14
@@ -1,12 +1,11 @@
|
||||
import _ from 'lodash';
|
||||
import { IosIPABuilder as createIPABuilder } from 'xdl';
|
||||
|
||||
import runShellAppBuilder from 'turtle/builders/utils/ios/shellAppBuilder';
|
||||
import { IContext } from 'turtle/builders/ios/context';
|
||||
import { logErrorOnce } from 'turtle/builders/utils/common';
|
||||
import * as fileUtils from 'turtle/builders/utils/file';
|
||||
import * as keychain from 'turtle/builders/utils/ios/keychain';
|
||||
import logger from 'turtle/logger/index';
|
||||
import { IContext } from 'turtle/builders/ios/context';
|
||||
import runShellAppBuilder from 'turtle/builders/utils/ios/shellAppBuilder';
|
||||
import { IJob } from 'turtle/job';
|
||||
|
||||
export default async function buildArchive(ctx: IContext, job: IJob) {
|
||||
@@ -19,7 +18,7 @@ export default async function buildArchive(ctx: IContext, job: IJob) {
|
||||
const manifest = await runShellAppBuilder(ctx, job);
|
||||
await buildAndSignIPA(ctx, job, keychainInfo.path, manifest);
|
||||
} catch (err) {
|
||||
logErrorOnce(err);
|
||||
logErrorOnce(err, ctx.logger);
|
||||
throw err;
|
||||
} finally {
|
||||
if (keychainInfo) {
|
||||
@@ -29,20 +28,20 @@ export default async function buildArchive(ctx: IContext, job: IJob) {
|
||||
}
|
||||
|
||||
async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, manifest: any) {
|
||||
const l = logger.withFields({ buildPhase: 'building and signing IPA' });
|
||||
l.info('building and signing IPA');
|
||||
ctx.logger = ctx.logger.child({ buildPhase: 'building and signing IPA' });
|
||||
ctx.logger.info('building and signing IPA');
|
||||
|
||||
const {
|
||||
credentials: { provisioningProfile, certPassword, teamId, password },
|
||||
config: { bundleIdentifier: _bundleIdentifierFromConfig },
|
||||
credentials: { provisioningProfile, certPassword, teamId },
|
||||
config: { bundleIdentifier: bundleIdentifierFromConfig },
|
||||
} = job;
|
||||
|
||||
const _bundleIdentifierFromManifest = _.get(manifest, 'ios.bundleIdentifier');
|
||||
const bundleIdentifier = _bundleIdentifierFromConfig || _bundleIdentifierFromManifest;
|
||||
const bundleIdentifierFromManifest = _.get(manifest, 'ios.bundleIdentifier');
|
||||
const bundleIdentifier = bundleIdentifierFromConfig || bundleIdentifierFromManifest;
|
||||
|
||||
const { provisioningProfilePath } = ctx;
|
||||
await fileUtils.writeBase64ToBinaryFile(provisioningProfilePath, provisioningProfile as string);
|
||||
l.info('saved provisioning profile to temporary path');
|
||||
ctx.logger.info('saved provisioning profile to temporary path');
|
||||
|
||||
const ipaBuilder = createIPABuilder({
|
||||
keychainPath,
|
||||
@@ -51,12 +50,11 @@ async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, m
|
||||
certPath: ctx.tempCertPath,
|
||||
certPassword,
|
||||
teamID: teamId,
|
||||
password,
|
||||
bundleIdentifier,
|
||||
workingDir: ctx.workingDir,
|
||||
workspacePath: ctx.workspacePath,
|
||||
manifest,
|
||||
});
|
||||
await ipaBuilder.build();
|
||||
|
||||
l.info(`done building and signing IPA`);
|
||||
ctx.logger.info(`done building and signing IPA`);
|
||||
}
|
||||
+69
-18
@@ -3,10 +3,10 @@ import * as path from 'path';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { IosShellApp } from 'xdl';
|
||||
|
||||
import { formatShellAppDirectory } from 'turtle/builders/utils/ios/workingdir';
|
||||
import config from 'turtle/config';
|
||||
import { IOS, PLATFORMS } from 'turtle/constants/index';
|
||||
import { IJob } from 'turtle/job';
|
||||
import { formatShellAppDirectory } from 'turtle/builders/utils/ios/workingdir';
|
||||
|
||||
export interface IContext {
|
||||
appDir: string;
|
||||
@@ -15,6 +15,7 @@ export interface IContext {
|
||||
baseArchiveDir: string;
|
||||
buildDir: string;
|
||||
fakeUploadBuildPath?: string;
|
||||
logger: any;
|
||||
outputPath: string;
|
||||
provisioningProfilePath: string;
|
||||
provisioningProfileDir: string;
|
||||
@@ -22,51 +23,99 @@ export interface IContext {
|
||||
tempCertPath: string;
|
||||
uploadPath: string;
|
||||
workingDir: string;
|
||||
workspacePath: string;
|
||||
applicationFilesSrc: string;
|
||||
}
|
||||
|
||||
const { DEFAULT_EXPOKIT_WORKSPACE_NAME } = IosShellApp;
|
||||
const { EXPOKIT_APP, EXPONENT_APP } = IosShellApp;
|
||||
const { BUILD_TYPES } = IOS;
|
||||
|
||||
export function createBuilderContext(job: IJob): IContext {
|
||||
export function createBuilderContext(job: IJob, logger: any): IContext {
|
||||
const { join } = path;
|
||||
const appUUID = uuidv4();
|
||||
const { manifest: { sdkVersion: _sdkVersionFromManifest = null } = {}, sdkVersion: _sdkVersionFromJob, config: { buildType } } = job;
|
||||
const {
|
||||
manifest: {
|
||||
sdkVersion: sdkVersionFromManifest = null,
|
||||
} = {},
|
||||
sdkVersion: sdkVersionFromJob,
|
||||
config: { buildType },
|
||||
} = job;
|
||||
|
||||
const sdkVersion = _sdkVersionFromJob || _sdkVersionFromManifest;
|
||||
const workingDir = formatShellAppDirectory(sdkVersion);
|
||||
const sdkVersion = sdkVersionFromJob || sdkVersionFromManifest;
|
||||
const workingDir = formatShellAppDirectory({ sdkVersion, buildType: buildType! });
|
||||
|
||||
const context: any = {
|
||||
appDir: join(config.directories.temporaryFilesRoot, appUUID),
|
||||
appUUID,
|
||||
workingDir,
|
||||
logger,
|
||||
};
|
||||
context.buildDir = join(context.appDir, 'build');
|
||||
context.provisioningProfileDir = join(context.appDir, 'provisioning');
|
||||
context.provisioningProfilePath = join(
|
||||
context.provisioningProfileDir,
|
||||
`${appUUID}.mobileprovision`
|
||||
`${appUUID}.mobileprovision`,
|
||||
);
|
||||
context.tempCertPath = join(context.appDir, 'cert.p12');
|
||||
context.baseArchiveDir = join(context.appDir, 'archive');
|
||||
|
||||
if (buildType === BUILD_TYPES.SIMULATOR) {
|
||||
context.outputPath = join(context.appDir, 'archive.tar.gz');
|
||||
context.uploadPath = join(context.appDir, 'archive.tar.gz');
|
||||
context.archiveDir = join(
|
||||
context.baseArchiveDir,
|
||||
'Release',
|
||||
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.app`
|
||||
if (buildType === BUILD_TYPES.CLIENT) {
|
||||
context.applicationFilesSrc = join(
|
||||
workingDir,
|
||||
'expo-client-build',
|
||||
'**',
|
||||
'*',
|
||||
);
|
||||
} else if (buildType === BUILD_TYPES.ARCHIVE) {
|
||||
} else {
|
||||
context.applicationFilesSrc = join(
|
||||
workingDir,
|
||||
'shellAppBase-builds',
|
||||
buildType as string,
|
||||
'**',
|
||||
'*',
|
||||
);
|
||||
}
|
||||
|
||||
if (buildType === BUILD_TYPES.ARCHIVE) {
|
||||
context.outputPath = join(context.appDir, 'archive.xcarchive');
|
||||
context.uploadPath = join(context.buildDir, 'archive.ipa');
|
||||
context.archiveDir = join(
|
||||
context.baseArchiveDir,
|
||||
'Release',
|
||||
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.xcarchive`,
|
||||
`${EXPOKIT_APP}.xcarchive`,
|
||||
'Products',
|
||||
'Applications',
|
||||
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.app`
|
||||
`${EXPOKIT_APP}.app`,
|
||||
);
|
||||
context.workspacePath = path.join(
|
||||
workingDir,
|
||||
'shellAppWorkspaces',
|
||||
'ios',
|
||||
'default',
|
||||
`${EXPOKIT_APP}.xcworkspace`,
|
||||
);
|
||||
} else if (buildType === BUILD_TYPES.CLIENT) {
|
||||
context.outputPath = join(context.appDir, 'archive.xcarchive');
|
||||
context.uploadPath = join(context.buildDir, 'archive.ipa');
|
||||
context.archiveDir = join(
|
||||
context.baseArchiveDir,
|
||||
`${EXPONENT_APP}.xcarchive`,
|
||||
'Products',
|
||||
'Applications',
|
||||
`${EXPONENT_APP}.app`,
|
||||
);
|
||||
context.workspacePath = path.join(
|
||||
workingDir,
|
||||
'ios',
|
||||
`${EXPONENT_APP}.xcworkspace`,
|
||||
);
|
||||
} else if (buildType === BUILD_TYPES.SIMULATOR) {
|
||||
context.outputPath = join(context.appDir, 'archive.tar.gz');
|
||||
context.uploadPath = join(context.appDir, 'archive.tar.gz');
|
||||
context.archiveDir = join(
|
||||
context.baseArchiveDir,
|
||||
'Release',
|
||||
`${EXPOKIT_APP}.app`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,7 +123,9 @@ export function createBuilderContext(job: IJob): IContext {
|
||||
const s3Filename = `${job.experienceName}-${appUUID}-${buildType}.${s3FileExtension}`;
|
||||
if (config.builder.fakeUpload) {
|
||||
const fakeUploadFilename = s3Filename.replace('/', '\\');
|
||||
context.fakeUploadBuildPath = job.fakeUploadBuildPath ? job.fakeUploadBuildPath : join(job.fakeUploadDir || config.directories.fakeUploadDir, fakeUploadFilename);
|
||||
context.fakeUploadBuildPath = job.fakeUploadBuildPath
|
||||
? job.fakeUploadBuildPath
|
||||
: join(job.fakeUploadDir || config.directories.fakeUploadDir, fakeUploadFilename);
|
||||
} else {
|
||||
context.s3FileKey = join(PLATFORMS.IOS, s3Filename);
|
||||
}
|
||||
|
||||
+17
-35
@@ -1,35 +1,39 @@
|
||||
import path from 'path';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import pick from 'lodash/pick';
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import buildArchive from 'turtle/builders/ios/archive';
|
||||
import { createBuilderContext, IContext } from 'turtle/builders/ios/context';
|
||||
import buildSimulator from 'turtle/builders/ios/simulator';
|
||||
import { logErrorOnce } from 'turtle/builders/utils/common';
|
||||
import prepareAdHocBuildCredentials from 'turtle/builders/utils/ios/adhocBuild';
|
||||
import { uploadBuildToS3 } from 'turtle/builders/utils/uploader';
|
||||
import { ensureCanBuildSdkVersion } from 'turtle/builders/utils/version';
|
||||
import config from 'turtle/config';
|
||||
import { IOS, PLATFORMS } from 'turtle/constants/index';
|
||||
import { IOS } from 'turtle/constants/index';
|
||||
import { IJob, IJobResult } from 'turtle/job';
|
||||
|
||||
const { BUILD_TYPES } = IOS;
|
||||
|
||||
let SUPPORTED_SDK_VERSIONS: Array<number>;
|
||||
export default async function iosBuilder(job: IJob, logger: any): Promise<IJobResult> {
|
||||
if (job.config.buildType !== BUILD_TYPES.CLIENT) {
|
||||
await ensureCanBuildSdkVersion(job);
|
||||
}
|
||||
|
||||
export default async function iosBuilder(job: IJob): Promise<IJobResult> {
|
||||
await ensureCanBuildSdkVersion(job);
|
||||
|
||||
const ctx = createBuilderContext(job);
|
||||
const ctx = createBuilderContext(job, logger);
|
||||
|
||||
try {
|
||||
await initBuilder(ctx);
|
||||
|
||||
const { buildType } = job.config;
|
||||
if (buildType === BUILD_TYPES.SIMULATOR) {
|
||||
await buildSimulator(ctx, job);
|
||||
} else if (buildType === BUILD_TYPES.ARCHIVE) {
|
||||
|
||||
if (buildType === BUILD_TYPES.CLIENT) {
|
||||
await prepareAdHocBuildCredentials(job, ctx.logger);
|
||||
}
|
||||
|
||||
if ([BUILD_TYPES.ARCHIVE, BUILD_TYPES.CLIENT].includes(buildType!)) {
|
||||
await buildArchive(ctx, job);
|
||||
} else if (buildType === BUILD_TYPES.SIMULATOR) {
|
||||
await buildSimulator(ctx, job);
|
||||
} else {
|
||||
throw new Error(`Unsupported iOS build type: ${buildType}`);
|
||||
}
|
||||
@@ -37,7 +41,7 @@ export default async function iosBuilder(job: IJob): Promise<IJobResult> {
|
||||
const artifactUrl = await uploadBuildToS3(ctx);
|
||||
return { artifactUrl };
|
||||
} catch (err) {
|
||||
logErrorOnce(err);
|
||||
logErrorOnce(err, ctx.logger);
|
||||
throw err;
|
||||
} finally {
|
||||
await cleanup(ctx);
|
||||
@@ -60,25 +64,3 @@ async function cleanup(ctx: IContext) {
|
||||
|
||||
const getTemporaryDirs = (ctx: IContext) =>
|
||||
Object.values(pick(ctx, ['appDir', 'provisioningProfileDir']));
|
||||
|
||||
async function ensureCanBuildSdkVersion(job: IJob) {
|
||||
if (config.builder.useLocalWorkingDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SUPPORTED_SDK_VERSIONS) {
|
||||
SUPPORTED_SDK_VERSIONS = await findSupportedSdkVersions();
|
||||
}
|
||||
|
||||
const targetMajorSdkVersion = ExponentTools.parseSdkMajorVersion(job.sdkVersion || job.manifest.sdkVersion);
|
||||
if (!SUPPORTED_SDK_VERSIONS.includes(targetMajorSdkVersion)) {
|
||||
throw new Error(`Unsupported SDK Version!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function findSupportedSdkVersions(): Promise<Array<number>> {
|
||||
const SDK_DIR_PREFIX = 'sdk';
|
||||
const files = await fs.readdir(path.join(config.directories.workingDir, PLATFORMS.IOS));
|
||||
const sdks = files.filter(file => file.startsWith(SDK_DIR_PREFIX));
|
||||
return sdks.map(sdk => parseInt(sdk.substr(SDK_DIR_PREFIX.length)));
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import runShellAppBuilder from 'turtle/builders/utils/ios/shellAppBuilder';
|
||||
import logger from 'turtle/logger';
|
||||
import { IContext } from 'turtle/builders/ios/context';
|
||||
import runShellAppBuilder from 'turtle/builders/utils/ios/shellAppBuilder';
|
||||
import { IJob } from 'turtle/job';
|
||||
|
||||
export default async function buildSimulator(ctx: IContext, job: IJob) {
|
||||
const l = logger.withFields({ buildPhase: 'running simulator builder' });
|
||||
l.info('running simulator build');
|
||||
ctx.logger = ctx.logger.child({ buildPhase: 'running simulator builder' });
|
||||
ctx.logger.info('running simulator build');
|
||||
await runShellAppBuilder(ctx, job);
|
||||
l.info('build complete');
|
||||
ctx.logger.info('build complete');
|
||||
}
|
||||
@@ -1,18 +1,17 @@
|
||||
import path from 'path';
|
||||
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import fs from 'fs-extra';
|
||||
import { AndroidKeystore } from 'xdl';
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import { Credentials } from 'xdl';
|
||||
|
||||
import * as sqs from 'turtle/aws/sqs';
|
||||
import { UPDATE_CREDENTIALS } from 'turtle/constants/build';
|
||||
import logger from 'turtle/logger';
|
||||
import { isOffline } from 'turtle/turtleContext';
|
||||
import { IAndroidCredentials, IJob } from 'turtle/job';
|
||||
import { isOffline } from 'turtle/turtleContext';
|
||||
|
||||
async function getOrCreateCredentials(jobData: IJob): Promise<IAndroidCredentials> {
|
||||
async function getOrCreateCredentials(jobData: IJob, logger: any): Promise<IAndroidCredentials> {
|
||||
if (!jobData.credentials) {
|
||||
const l = logger.withFields({ buildPhase: 'generating keystore' });
|
||||
const l = logger.child({ buildPhase: 'generating keystore' });
|
||||
const credentials: any = {};
|
||||
l.info('Creating keystore');
|
||||
const keystoreFilename = `/tmp/keystore-${jobData.id}.tmp.jks`;
|
||||
@@ -21,13 +20,15 @@ async function getOrCreateCredentials(jobData: IJob): Promise<IAndroidCredential
|
||||
credentials.keystoreAlias = Buffer.from(jobData.experienceName).toString('base64');
|
||||
const androidPackage = jobData.manifest.android.package;
|
||||
|
||||
await AndroidKeystore.createKeystore({
|
||||
keystorePassword: credentials.keystorePassword,
|
||||
keyPassword: credentials.keyPassword,
|
||||
keystoreFilename,
|
||||
keystoreAlias: credentials.keystoreAlias,
|
||||
await Credentials.Android.createKeystore(
|
||||
{
|
||||
keystorePath: keystoreFilename,
|
||||
keystorePassword: credentials.keystorePassword,
|
||||
keyPassword: credentials.keyPassword,
|
||||
keyAlias: credentials.keystoreAlias,
|
||||
},
|
||||
androidPackage,
|
||||
});
|
||||
);
|
||||
|
||||
credentials.keystore = (await fs.readFile(keystoreFilename)).toString('base64');
|
||||
l.info('Keystore created successfully');
|
||||
@@ -42,7 +43,7 @@ async function getOrCreateCredentials(jobData: IJob): Promise<IAndroidCredential
|
||||
} else {
|
||||
await sqs.sendMessage(jobData.id, UPDATE_CREDENTIALS, {
|
||||
...credentials,
|
||||
});
|
||||
}, l);
|
||||
l.info('Keystore sent to storage successfully');
|
||||
}
|
||||
await fs.unlink(keystoreFilename);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import path from 'path';
|
||||
import config from 'turtle/config';
|
||||
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import { IShellAppDirectoryConfig } from 'turtle/builders/utils/workingdir';
|
||||
import config from 'turtle/config';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
export function formatShellAppDirectory(sdkVersion: string) {
|
||||
export function formatShellAppDirectory({ sdkVersion }: IShellAppDirectoryConfig) {
|
||||
const majorSdkVersion = ExponentTools.parseSdkMajorVersion(sdkVersion);
|
||||
return config.builder.useLocalWorkingDir
|
||||
? path.join(config.directories.workingDir, 'local')
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as url from 'url';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import { IJob } from 'turtle/job';
|
||||
|
||||
export function getExperienceUrl(job: IJob) {
|
||||
@@ -18,7 +17,7 @@ export function getExperienceUrl(job: IJob) {
|
||||
|
||||
const alreadyLoggedError = Symbol('alreadyLoggedError');
|
||||
|
||||
export function logErrorOnce(err: any) {
|
||||
export function logErrorOnce(err: any, logger: any) {
|
||||
if (!err[alreadyLoggedError]) {
|
||||
logger.error(err.stack);
|
||||
err[alreadyLoggedError] = true;
|
||||
|
||||
@@ -18,15 +18,15 @@ export async function resizeIconWithSharpAsync(iconSizePx: number, iconFilename:
|
||||
await fs.writeFile(filename, buffer);
|
||||
}
|
||||
|
||||
export async function getImageDimensionsWithSharpAsync(basename: string, dirname: string) {
|
||||
const filename = path.join(dirname, basename);
|
||||
export async function getImageDimensionsWithSharpAsync(dirname: string, filename: string) {
|
||||
const filepath = path.join(dirname, filename);
|
||||
|
||||
// PLEASE DON'T REMOVE THE FOLLOWING LINE (sharp caches files taking path as the cache key)
|
||||
sharp.cache(false);
|
||||
|
||||
try {
|
||||
const meta = await sharp(filename).metadata();
|
||||
return [meta.width, meta.height];
|
||||
const { width, height } = await sharp(filepath).metadata();
|
||||
return { width, height };
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import path from 'path';
|
||||
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import fs from 'fs-extra';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { IosCodeSigning } from 'xdl';
|
||||
|
||||
import * as sqs from 'turtle/aws/sqs';
|
||||
import BuildError, { BuildErrorReason } from 'turtle/builders/BuildError';
|
||||
import { UPDATE_CREDENTIALS } from 'turtle/constants/build';
|
||||
import { IJob } from 'turtle/job';
|
||||
import { isOffline } from 'turtle/turtleContext';
|
||||
|
||||
// tslint:disable-next-line:no-var-requires
|
||||
const travelingFastlane = process.platform === 'darwin' ? require('@expo/traveling-fastlane-darwin')() : null;
|
||||
|
||||
async function prepareAdHocBuildCredentials(job: IJob, logger: any) {
|
||||
if (process.platform !== 'darwin') {
|
||||
throw new Error('This function should be called only on macOS!');
|
||||
}
|
||||
|
||||
const { bundleIdentifier } = job.config;
|
||||
const { certP12, certPassword, teamId, appleSession, udids } = job.credentials;
|
||||
|
||||
const certSerialNumber = IosCodeSigning.findP12CertSerialNumber(certP12, certPassword);
|
||||
const args = [
|
||||
teamId,
|
||||
udids!.join(','),
|
||||
bundleIdentifier,
|
||||
certSerialNumber || '__last__',
|
||||
];
|
||||
|
||||
try {
|
||||
const credentials = await runFastlaneAction(
|
||||
travelingFastlane.manageAdHocProvisioningProfile,
|
||||
args,
|
||||
{ env: { FASTLANE_SESSION: appleSession } },
|
||||
);
|
||||
if (isEmpty(credentials)) {
|
||||
if (!job.credentials.provisioningProfile) {
|
||||
// a user has generated the provisioning profile for his app, but he probably deleted it from Expo servers, ...
|
||||
throw new BuildError(
|
||||
BuildErrorReason.PROVISIONING_PROFILE_MISSING,
|
||||
`Provisioning profile is missing on Expo servers.`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('New ad hoc provisioning profile successfully created');
|
||||
job.credentials.provisioningProfile = credentials.provisioningProfile;
|
||||
|
||||
if (isOffline()) {
|
||||
const provisioningProfilePath = path.join(job.projectDir, `adhoc.mobileprovision`);
|
||||
await fs.writeFile(provisioningProfilePath, Buffer.from(credentials.provisioningProfile, 'base64'));
|
||||
logger.info(`Saved provisioning profile to ${provisioningProfilePath}`);
|
||||
} else {
|
||||
await sqs.sendMessage(job.id, UPDATE_CREDENTIALS, {
|
||||
provisioningProfileId: credentials.provisioningProfileId,
|
||||
provisioningProfile: credentials.provisioningProfile,
|
||||
}, logger);
|
||||
logger.info('Ad Hoc provisioning profile sent to storage successfully');
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof BuildError) {
|
||||
logger.error('Apple Session expired! Please authenticate again using expo-cli.');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function runFastlaneAction(action: string, args: string[], { env }: any) {
|
||||
const { stderr } = await spawnAsync(
|
||||
action,
|
||||
args,
|
||||
{
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
env: { ...process.env, ...env },
|
||||
},
|
||||
);
|
||||
const { result, ...rest } = JSON.parse(stderr.trim());
|
||||
if (result === 'failure') {
|
||||
const { reason, rawDump, type } = rest;
|
||||
let errorMsg = `Reason: ${reason}`;
|
||||
if (rawDump) {
|
||||
errorMsg += `, raw: ${JSON.stringify(rawDump)}`;
|
||||
}
|
||||
if (type === 'session-expired') {
|
||||
throw new BuildError(BuildErrorReason.SESSION_EXPIRED, errorMsg);
|
||||
} else {
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
} else {
|
||||
return rest;
|
||||
}
|
||||
}
|
||||
|
||||
export default prepareAdHocBuildCredentials;
|
||||
@@ -1,29 +1,27 @@
|
||||
import { IosKeychain } from 'xdl';
|
||||
|
||||
import * as fileUtils from 'turtle/builders/utils/file';
|
||||
import logger from 'turtle/logger';
|
||||
import { IContext } from 'turtle/builders/ios/context';
|
||||
import * as fileUtils from 'turtle/builders/utils/file';
|
||||
|
||||
interface IKeychain {
|
||||
path: string;
|
||||
}
|
||||
|
||||
|
||||
export async function create(ctx: IContext): Promise<IKeychain> {
|
||||
const l = logger.withFields({ buildPhase: 'creating keychain' });
|
||||
const l = ctx.logger.child({ buildPhase: 'creating keychain' });
|
||||
try {
|
||||
l.info('creating keychain...');
|
||||
const keychainInfo = await IosKeychain.createKeychain(ctx.appUUID, false);
|
||||
l.info('done creating keychain');
|
||||
return keychainInfo;
|
||||
} catch (err) {
|
||||
l.error('unable to create keychain');
|
||||
l.error({ err }, 'unable to create keychain');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function remove(ctx: IContext, keychainPath: string) {
|
||||
const l = logger.withFields({ buildPhase: 'deleting keychain' });
|
||||
const l = ctx.logger.child({ buildPhase: 'deleting keychain' });
|
||||
try {
|
||||
l.info('delete keychain...');
|
||||
const keychainInfo = await IosKeychain.deleteKeychain({
|
||||
@@ -33,7 +31,7 @@ export async function remove(ctx: IContext, keychainPath: string) {
|
||||
l.info('done deleting keychain');
|
||||
return keychainInfo;
|
||||
} catch (err) {
|
||||
l.error('unable to delete keychain');
|
||||
l.error({ err }, 'unable to delete keychain');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +48,7 @@ export async function importCert(
|
||||
certPassword,
|
||||
}: { keychainPath: string; certP12: string; certPassword: string },
|
||||
) {
|
||||
const l = logger.withFields({ buildPhase: 'importing certificate into keychain' });
|
||||
const l = ctx.logger.child({ buildPhase: 'importing certificate into keychain' });
|
||||
try {
|
||||
l.info('importing distribution certificate into keychain...');
|
||||
const { tempCertPath: certPath } = ctx;
|
||||
@@ -58,7 +56,7 @@ export async function importCert(
|
||||
await IosKeychain.importIntoKeychain({ keychainPath, certPath, certPassword });
|
||||
l.info('done importing distribution certificate into keychain');
|
||||
} catch (err) {
|
||||
l.error('unable to import distribution certificate into keychain');
|
||||
l.error({ err }, 'unable to import distribution certificate into keychain');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -2,51 +2,52 @@ import path from 'path';
|
||||
import util from 'util';
|
||||
|
||||
import copy from 'copy';
|
||||
import { ImageUtils, IosShellApp } from 'xdl';
|
||||
import _ from 'lodash';
|
||||
import { ImageUtils, IosShellApp } from 'xdl';
|
||||
|
||||
import { IContext } from 'turtle/builders/ios/context';
|
||||
import * as commonUtils from 'turtle/builders/utils/common';
|
||||
import * as imageHelpers from 'turtle/builders/utils/image';
|
||||
import logger from 'turtle/logger/index';
|
||||
import { IContext } from 'turtle/builders/ios/context';
|
||||
import { IOS } from 'turtle/constants/index';
|
||||
import { IJob } from 'turtle/job';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
const copyAsync = util.promisify(copy);
|
||||
|
||||
export default async function runShellAppBuilder(ctx: IContext, job: IJob): Promise<any> {
|
||||
const { config: jobConfig, manifest, sdkVersion: _sdkVersionFromJob } = job;
|
||||
const { config: jobConfig, manifest, sdkVersion: sdkVersionFromJob } = job;
|
||||
const { buildType, releaseChannel } = jobConfig;
|
||||
const sdkVersion = _.get(manifest, 'sdkVersion', _sdkVersionFromJob);
|
||||
const applicationFilesSrc = path.join(
|
||||
ctx.workingDir,
|
||||
'shellAppBase-builds',
|
||||
buildType as string,
|
||||
'**',
|
||||
'*',
|
||||
);
|
||||
await copyAsync(applicationFilesSrc, ctx.baseArchiveDir);
|
||||
const sdkVersion = _.get(manifest, 'sdkVersion', sdkVersionFromJob);
|
||||
|
||||
logger.info(
|
||||
await copyAsync(ctx.applicationFilesSrc, ctx.baseArchiveDir);
|
||||
|
||||
ctx.logger.info(
|
||||
{ buildPhase: 'icons setup' },
|
||||
'ImageUtils: setting image functions to alternative sharp implementations',
|
||||
);
|
||||
ImageUtils.setResizeImageFunction(imageHelpers.resizeIconWithSharpAsync);
|
||||
ImageUtils.setGetImageDimensionsFunction(imageHelpers.getImageDimensionsWithSharpAsync);
|
||||
const shellAppParams = {
|
||||
url: commonUtils.getExperienceUrl(job),
|
||||
sdkVersion,
|
||||
action: 'configure',
|
||||
type: buildType,
|
||||
releaseChannel,
|
||||
archivePath: ctx.archiveDir,
|
||||
privateConfigData: job.config,
|
||||
verbose: true,
|
||||
output: ctx.outputPath,
|
||||
expoSourcePath: path.join(ctx.workingDir, PLATFORMS.IOS),
|
||||
expoSourcePath: path.join(ctx.workingDir, 'ios'),
|
||||
manifest,
|
||||
output: ctx.outputPath,
|
||||
verbose: true,
|
||||
};
|
||||
if (buildType === IOS.BUILD_TYPES.CLIENT) {
|
||||
Object.assign(shellAppParams, {
|
||||
workspacePath: path.join(ctx.workingDir, 'ios'),
|
||||
});
|
||||
} else {
|
||||
Object.assign(shellAppParams, {
|
||||
url: commonUtils.getExperienceUrl(job),
|
||||
releaseChannel,
|
||||
sdkVersion,
|
||||
});
|
||||
}
|
||||
|
||||
logger.info({ buildPhase: 'configuring NSBundle' }, 'configuring NSBundle...');
|
||||
ctx.logger.info({ buildPhase: 'configuring NSBundle' }, 'configuring NSBundle...');
|
||||
return await IosShellApp.configureAndCopyArchiveAsync(shellAppParams);
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
import path from 'path';
|
||||
import { ExponentTools } from 'xdl';
|
||||
import config from 'turtle/config';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
export function formatShellAppDirectory(sdkVersion: string) {
|
||||
const majorVersion = ExponentTools.parseSdkMajorVersion(sdkVersion);
|
||||
return config.builder.useLocalWorkingDir
|
||||
? path.join(config.directories.workingDir, 'local')
|
||||
: path.join(config.directories.workingDir, PLATFORMS.IOS, `sdk${majorVersion}`);
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import { IShellAppDirectoryConfig } from 'turtle/builders/utils/workingdir';
|
||||
import config from 'turtle/config';
|
||||
import { IOS, PLATFORMS } from 'turtle/constants';
|
||||
|
||||
export function formatShellAppDirectory({ sdkVersion, buildType }: IShellAppDirectoryConfig) {
|
||||
const { workingDir } = config.directories;
|
||||
if (buildType === IOS.BUILD_TYPES.CLIENT) {
|
||||
return path.join(workingDir, PLATFORMS.IOS, 'client');
|
||||
} else if (config.builder.useLocalWorkingDir) {
|
||||
return path.join(workingDir, 'local');
|
||||
} else {
|
||||
const majorVersion = ExponentTools.parseSdkMajorVersion(sdkVersion);
|
||||
return path.join(workingDir, PLATFORMS.IOS, `sdk${majorVersion}`);
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,24 @@ import * as fs from 'fs-extra';
|
||||
|
||||
import { uploadFile } from 'turtle/aws/s3';
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
interface IUploadCtx {
|
||||
fakeUploadBuildPath?: string;
|
||||
s3FileKey?: string;
|
||||
uploadPath: string;
|
||||
logger: any;
|
||||
}
|
||||
|
||||
export async function uploadBuildToS3(ctx: IUploadCtx) {
|
||||
if (config.builder.fakeUpload) {
|
||||
const l = logger.withFields({ buildPhase: 'copying build artifact' });
|
||||
const l = ctx.logger.child({ buildPhase: 'copying build artifact' });
|
||||
const { fakeUploadBuildPath, uploadPath } = ctx;
|
||||
l.info('copying build to fake upload directory');
|
||||
await fs.copy(uploadPath, fakeUploadBuildPath as string);
|
||||
l.info(`copied build to ${fakeUploadBuildPath}`);
|
||||
return fakeUploadBuildPath as string;
|
||||
} else {
|
||||
const l = logger.withFields({ buildPhase: 'uploading to S3' });
|
||||
const l = ctx.logger.child({ buildPhase: 'uploading to S3' });
|
||||
l.info('uploading build artifact to S3');
|
||||
const { Location: fileLocation } = await uploadFile({
|
||||
key: ctx.s3FileKey as string,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import { IJob } from 'turtle/job';
|
||||
|
||||
let SUPPORTED_SDK_VERSIONS: number[];
|
||||
|
||||
export async function ensureCanBuildSdkVersion(job: IJob) {
|
||||
if (config.builder.useLocalWorkingDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SUPPORTED_SDK_VERSIONS) {
|
||||
SUPPORTED_SDK_VERSIONS = await findSupportedSdkVersions(job.platform);
|
||||
}
|
||||
|
||||
const targetMajorSdkVersion = ExponentTools.parseSdkMajorVersion(job.sdkVersion || job.manifest.sdkVersion);
|
||||
if (!SUPPORTED_SDK_VERSIONS.includes(targetMajorSdkVersion)) {
|
||||
throw new Error(`Unsupported SDK Version!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function findSupportedSdkVersions(platform: 'android' | 'ios'): Promise<number[]> {
|
||||
const SDK_DIR_PREFIX = 'sdk';
|
||||
const files = await fs.readdir(path.join(config.directories.workingDir, platform));
|
||||
const sdks = files.filter((file) => file.startsWith(SDK_DIR_PREFIX));
|
||||
return sdks.map((sdk) => parseInt(sdk.substr(SDK_DIR_PREFIX.length), 10));
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { IOS } from 'turtle/constants';
|
||||
|
||||
export interface IShellAppDirectoryConfig {
|
||||
sdkVersion: string;
|
||||
buildType?: IOS.BUILD_TYPES;
|
||||
}
|
||||
+14
-10
@@ -1,14 +1,14 @@
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import { env, envNum, envOptional, envTransform } from 'turtle/utils/env';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
import { env, envNum, envOptional, envTransform } from 'turtle/utils/env';
|
||||
|
||||
export default {
|
||||
env: env('NODE_ENV'),
|
||||
hostname: env('HOSTNAME', os.hostname()),
|
||||
deploymentEnv: env('ENVIRONMENT'),
|
||||
platform: env('PLATFORM', PLATFORMS.IOS),
|
||||
platform: env('PLATFORM', PLATFORMS.IOS) as PLATFORMS,
|
||||
aws: {
|
||||
accessKeyId: env('AWS_ACCESS_KEY_ID'),
|
||||
secretAccessKey: env('AWS_SECRET_ACCESS_KEY'),
|
||||
@@ -47,11 +47,9 @@ export default {
|
||||
client: {
|
||||
level: env('CLIENT_LOGGER_LEVEL', 'info'),
|
||||
},
|
||||
loggly: {
|
||||
token: env('LOGGLY_TOKEN', ''),
|
||||
subdomain: env('LOGGLY_SUBDOMAIN', ''),
|
||||
buffer: envNum('LOGGLY_BUFFER', 100),
|
||||
},
|
||||
},
|
||||
google: {
|
||||
credentials: env('GOOGLE_APPLICATION_CREDENTIALS', ''),
|
||||
},
|
||||
sentry: {
|
||||
dsn: env('SENTRY_DSN', ''),
|
||||
@@ -66,7 +64,7 @@ export default {
|
||||
skipCleanup: envTransform(
|
||||
'TURTLE_SKIP_CLEANUP',
|
||||
'0',
|
||||
(val: string) => env('NODE_ENV') === 'development' && val === '1'
|
||||
(val: string) => env('NODE_ENV') === 'development' && val === '1',
|
||||
),
|
||||
fakeUpload: envTransform('TURTLE_FAKE_UPLOAD', '0', (val: string) => val === '1'),
|
||||
maxJobTimeMs: envNum('TURTLE_MAX_JOB_TIME_MS', 60 * 60 * 1000),
|
||||
@@ -77,9 +75,15 @@ export default {
|
||||
workingDir: env('TURTLE_WORKING_DIR_PATH', path.join(__dirname, '../workingdir')),
|
||||
shellTarballsDir: env('TURTLE_SHELL_TARBALLS_DIR', path.join(__dirname, '../shellTarballs')),
|
||||
artifactsDir: env('TURTLE_ARTIFACTS_DIR', path.join(__dirname, '../artifacts')),
|
||||
androidDependenciesDir: env('TURTLE_ANDROID_DEPENDENCIES_DIR', path.join(os.homedir(), '.turtle/androidDependencies')),
|
||||
androidDependenciesDir: env(
|
||||
'TURTLE_ANDROID_DEPENDENCIES_DIR',
|
||||
path.join(os.homedir(), '.turtle/androidDependencies'),
|
||||
),
|
||||
tempS3LogsDir: env('TURTLE_TEMP_S3_LOGS_DIR', '/tmp/logs'),
|
||||
fakeUploadDir: envOptional('TURTLE_FAKE_UPLOAD_DIR'),
|
||||
temporaryFilesRoot: env('TURTLE_TEMPORARY_FILES_DIR', path.join(os.platform() === 'darwin' ? '/private' : '/', 'tmp', 'turtle')),
|
||||
temporaryFilesRoot: env(
|
||||
'TURTLE_TEMPORARY_FILES_DIR',
|
||||
path.join(os.platform() === 'darwin' ? '/private' : '/', 'tmp', 'turtle'),
|
||||
),
|
||||
},
|
||||
};
|
||||
@@ -5,7 +5,7 @@ export enum JOB_STATES {
|
||||
FINISHED = 'finished',
|
||||
ERRORED = 'errored',
|
||||
SENT_TO_QUEUE = 'sent-to-queue',
|
||||
};
|
||||
}
|
||||
|
||||
export const UPDATE_CREDENTIALS = 'update-credentials';
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import * as BUILD from './build';
|
||||
import * as IOS from './ios';
|
||||
|
||||
export const PLATFORMS = {
|
||||
IOS: 'ios',
|
||||
ANDROID: 'android',
|
||||
};
|
||||
export enum PLATFORMS {
|
||||
IOS = 'ios',
|
||||
ANDROID = 'android',
|
||||
}
|
||||
|
||||
export { IOS, BUILD };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const BUILD_TYPES = {
|
||||
ARCHIVE: 'archive',
|
||||
SIMULATOR: 'simulator',
|
||||
};
|
||||
export enum BUILD_TYPES {
|
||||
ARCHIVE = 'archive',
|
||||
CLIENT = 'client',
|
||||
SIMULATOR = 'simulator',
|
||||
}
|
||||
+6
-3
@@ -1,3 +1,5 @@
|
||||
import { IOS } from 'turtle/constants';
|
||||
|
||||
export interface IJob {
|
||||
platform: 'android' | 'ios';
|
||||
experienceName: string;
|
||||
@@ -8,7 +10,7 @@ export interface IJob {
|
||||
releaseChannel: string;
|
||||
publicUrl?: string;
|
||||
// ios
|
||||
buildType?: string;
|
||||
buildType?: IOS.BUILD_TYPES;
|
||||
bundleIdentifier?: string;
|
||||
};
|
||||
credentials: {
|
||||
@@ -16,13 +18,14 @@ export interface IJob {
|
||||
keystore?: string;
|
||||
keystoreAlias?: string;
|
||||
keystorePassword?: string;
|
||||
// ios
|
||||
keyPassword?: string;
|
||||
// ios
|
||||
certP12?: string;
|
||||
certPassword?: string;
|
||||
provisioningProfile?: string;
|
||||
password?: string;
|
||||
teamId?: string;
|
||||
appleSession?: string;
|
||||
udids?: string[];
|
||||
};
|
||||
projectDir: string;
|
||||
fakeUploadDir?: string;
|
||||
|
||||
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user