mirror of
https://github.com/expo/turtle.git
synced 2026-11-14 19:12:55 -10:00
Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
226e0fe899 | ||
|
|
6fe9106de6 | ||
|
|
7ae44b4093 | ||
|
|
34e397352b | ||
|
|
1f8017de54 | ||
|
|
671c7e8ae6 | ||
|
|
69fd839049 | ||
|
|
55f0a94560 | ||
|
|
53020e7d73 | ||
|
|
8d78d55ae2 | ||
|
|
483df86e7c |
No files matched your search
@@ -1,25 +1,21 @@
|
||||
version: 2
|
||||
|
||||
machine:
|
||||
node:
|
||||
# Circle will use node v4.0.0
|
||||
version: 4.0.0
|
||||
|
||||
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
|
||||
- image: circleci/node:8.12
|
||||
steps:
|
||||
- checkout
|
||||
- run: yarn
|
||||
- run: ./test-ci-unit.sh
|
||||
- run: ./scripts/testUnitCI.sh
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
@@ -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,3 +6,6 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
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]
|
||||
|
||||
## [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 `[email protected]`.
|
||||
|
||||
## [0.5.0] - 2019-01-16
|
||||
### Added
|
||||
- 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.
|
||||
|
||||
## [0.4.3] - 2019-01-09
|
||||
### Changed
|
||||
- Update iOS shell app for SDK32.
|
||||
- Update Android shell app for SDK32.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Community Code of Conduct
|
||||
|
||||
The Expo team is committed to fostering an open and welcoming environment.
|
||||
|
||||
**Our Community Guidelines can be found here:**
|
||||
|
||||
https://expo.io/guidelines
|
||||
@@ -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
-9
@@ -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
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,13 +1,39 @@
|
||||
# Turtle CLI
|
||||
|
||||
Turtle CLI is a command line interface for building Expo standalone apps. You can use it both on your CI and your private computer.
|
||||
[](https://circleci.com/gh/expo/turtle)
|
||||
[](https://npmjs.com/package/turtle-cli)
|
||||
[](https://github.com/expo/turtle/blob/master/LICENSE)
|
||||
|
||||
### Glossary
|
||||
[](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.
|
||||
|
||||
- [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.
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"allowSyntheticDefaultImports": false,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"turtle/*": ["src/*"],
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "build"]
|
||||
}
|
||||
+14
-14
@@ -1,23 +1,22 @@
|
||||
{
|
||||
"name": "turtle-cli",
|
||||
"version": "0.4.3",
|
||||
"version": "0.5.6",
|
||||
"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'",
|
||||
"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} .",
|
||||
@@ -29,12 +28,13 @@
|
||||
"test:smoke": "yarn test --config jest/smoke-config.json --forceExit",
|
||||
"test:smoke: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"
|
||||
},
|
||||
@@ -60,6 +60,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 +78,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",
|
||||
@@ -92,6 +91,7 @@
|
||||
"jest": "^23.6.0",
|
||||
"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",
|
||||
|
||||
@@ -12,7 +12,13 @@ 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
|
||||
if command -v run >/dev/null 2>&1 ; then
|
||||
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
|
||||
else
|
||||
# outside of universe aws credentials might not be set
|
||||
if ! aws iam get-user >/dev/null 2>&1 ; then aws configure ; fi
|
||||
aws s3 cp $TARBALL_URI $ARTIFACTS_DIR/$SDK_VERSION.tar.gz --no-progress
|
||||
fi
|
||||
tar zxf $ARTIFACTS_DIR/$SDK_VERSION.tar.gz -C $WORKING_DIR/$SDK_VERSION
|
||||
rm -rf $TARBALL_URI $ARTIFACTS_DIR/$SDK_VERSION.tar.gz
|
||||
done
|
||||
+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"
|
||||
@@ -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
|
||||
|
||||
@@ -12,7 +12,13 @@ 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
|
||||
if command -v run >/dev/null 2>&1 ; then
|
||||
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
|
||||
else
|
||||
# outside of universe aws credentials might not be set
|
||||
if ! aws iam get-user >/dev/null 2>&1 ; then aws configure ; fi
|
||||
aws s3 cp $TARBALL_URI $ARTIFACTS_DIR/$SDK_VERSION.tar.gz --no-progress
|
||||
fi
|
||||
tar zxf $ARTIFACTS_DIR/$SDK_VERSION.tar.gz -C $WORKING_DIR/$SDK_VERSION
|
||||
rm -rf $TARBALL_URI $ARTIFACTS_DIR/$SDK_VERSION.tar.gz
|
||||
done
|
||||
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
|
||||
@@ -20,6 +20,9 @@ export API_PORT="3000"
|
||||
export REDIS_URL="noop"
|
||||
export REDIS_CONFIG_URL="noop"
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
||||
$ROOT_DIR/scripts/android/fetchRemoteAndroidTarball.sh
|
||||
|
||||
yarn test:smoke:android
|
||||
smoke_result=$?
|
||||
|
||||
@@ -18,7 +18,8 @@ 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 )"
|
||||
$ROOT_DIR/scripts/ios/fetchRemoteIosTarball.sh
|
||||
|
||||
yarn test:smoke:ios
|
||||
smoke_result=$?
|
||||
File renamed without changes.
@@ -1 +1 @@
|
||||
s3://exp-artifacts/android-shell-builder-55e2cc952dc8bae3ebba8aaaab6eb1199ac7b0d0.tar.gz
|
||||
s3://exp-artifacts/android-shell-builder-cb03fdc12dc74f699a6d3f22219cbcd30e01ac46.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-b385131fa696773bf82e4e6345e147f71ee2fa96.tar.gz
|
||||
@@ -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';
|
||||
}),
|
||||
}));
|
||||
@@ -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;
|
||||
|
||||
+29
-23
@@ -1,16 +1,22 @@
|
||||
import aws from 'aws-sdk';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { EXTRA_DIMENSIONS, GROUP_HASH } from 'turtle/aws/cloudwatch/constants';
|
||||
import calculateStatistics from 'turtle/aws/cloudwatch/statistics';
|
||||
import {
|
||||
IMetric,
|
||||
IMetricConfiguration,
|
||||
IMetricData,
|
||||
IMetricsChunk,
|
||||
MetricRegistrationObject,
|
||||
} from 'turtle/aws/cloudwatch/types';
|
||||
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';
|
||||
|
||||
interface State {
|
||||
metrics: Array<Metric>;
|
||||
interface IState {
|
||||
metrics: IMetric[];
|
||||
registeredMetrics: {
|
||||
[key: string]: MetricConfiguration;
|
||||
[key: string]: IMetricConfiguration;
|
||||
};
|
||||
intervalId: NodeJS.Timer | null;
|
||||
}
|
||||
@@ -19,7 +25,7 @@ const SUCCESS_SUFFIX = '.success';
|
||||
const FAIL_SUFFIX = '.fail';
|
||||
const CHUNK_SIZE = 20;
|
||||
|
||||
const state: State = {
|
||||
const state: IState = {
|
||||
metrics: [],
|
||||
registeredMetrics: {},
|
||||
intervalId: null,
|
||||
@@ -46,7 +52,7 @@ export function addMetricData({
|
||||
success = true,
|
||||
explicitName = false,
|
||||
extraDimensions = [],
|
||||
}: MetricData) {
|
||||
}: IMetricData) {
|
||||
if (config.cloudwatch.disabled) {
|
||||
return;
|
||||
}
|
||||
@@ -57,7 +63,7 @@ export function addMetricData({
|
||||
return;
|
||||
}
|
||||
const { unit } = registeredMetric;
|
||||
const metric: Metric = {
|
||||
const metric: IMetric = {
|
||||
MetricName: metricName,
|
||||
Unit: unit,
|
||||
Value: value,
|
||||
@@ -114,7 +120,7 @@ function _dumpMetrics() {
|
||||
logger.trace(
|
||||
'[cloudwatch] lenghts for state.metrics=%d, state.registeredMetrics=%d',
|
||||
state.metrics.length,
|
||||
Object.keys(state.registeredMetrics).length
|
||||
Object.keys(state.registeredMetrics).length,
|
||||
);
|
||||
|
||||
_addEmptyMetrics(state.metrics);
|
||||
@@ -124,7 +130,7 @@ function _dumpMetrics() {
|
||||
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,44 +140,44 @@ 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.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);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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};
|
||||
@@ -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,
|
||||
|
||||
+4
-4
@@ -2,11 +2,11 @@ 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',
|
||||
@@ -26,7 +26,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,7 +35,7 @@ export async function receiveMessage(priority: string) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
logger.error('Receive error', err);
|
||||
throw err;
|
||||
});
|
||||
@@ -67,7 +67,7 @@ export async function changeMessageVisibility(priority: string, receiptHandle: s
|
||||
export function changeMessageVisibilityRecurring(priority: string, receiptHandle: string, jobId: string) {
|
||||
return setInterval(() => {
|
||||
if (getCurrentJobId() === jobId) {
|
||||
changeMessageVisibility(priority, receiptHandle).catch(err => {
|
||||
changeMessageVisibility(priority, receiptHandle).catch((err) => {
|
||||
logger.warn('Error at change msg visibility', err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
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) => {
|
||||
@@ -15,15 +15,15 @@ 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)',
|
||||
);
|
||||
}
|
||||
|
||||
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,20 +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)'
|
||||
)
|
||||
.option(
|
||||
'--push-p12-path <push.p12>',
|
||||
'path to your Push Notification Certificate P12 (please provide password as EXPO_IOS_PUSH_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({
|
||||
@@ -40,7 +36,7 @@ export default (program: any, setCommonCommandOptions: any) => {
|
||||
builder,
|
||||
platform: PLATFORMS.IOS,
|
||||
os: 'darwin',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -64,16 +60,16 @@ const prepareCredentials = async (cmd: any) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { teamId, distP12Path, pushP12Path, provisioningProfilePath } = cmd;
|
||||
const { teamId, distP12Path, provisioningProfilePath } = cmd;
|
||||
const certPassword = process.env.EXPO_IOS_DIST_P12_PASSWORD;
|
||||
const pushPassword = process.env.EXPO_IOS_PUSH_P12_PASSWORD;
|
||||
|
||||
const credentialsExist =
|
||||
teamId && distP12Path && certPassword && pushP12Path && pushPassword && provisioningProfilePath;
|
||||
teamId && distP12Path && certPassword && provisioningProfilePath;
|
||||
|
||||
if (!credentialsExist) {
|
||||
throw new ErrorWithCommandHelp(
|
||||
'Please provide all required credentials - Apple Team ID, Distribution Certificate P12 (with password), Push Notification Certificate P12 (with password) and Provisioning Profile'
|
||||
'Please provide all required credentials'
|
||||
+ '- Apple Team ID, Distribution Certificate P12 (with password) and Provisioning Profile',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,8 +77,6 @@ const prepareCredentials = async (cmd: any) => {
|
||||
teamId,
|
||||
certP12: (await fs.readFile(distP12Path)).toString('base64'),
|
||||
certPassword,
|
||||
pushP12: (await fs.readFile(pushP12Path)).toString('base64'),
|
||||
pushPassword,
|
||||
provisioningProfile: (await fs.readFile(provisioningProfilePath)).toString('base64'),
|
||||
};
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
export class ErrorWithProgramHelp extends Error {}
|
||||
export class ErrorWithCommandHelp extends Error {}
|
||||
@@ -1,9 +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';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
const l = logger.withFields({ buildPhase: 'setting up environment' });
|
||||
|
||||
@@ -16,7 +17,7 @@ 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));
|
||||
};
|
||||
@@ -30,7 +31,7 @@ async function setupAction(program: any, cmd: any, platform: string, os?: string
|
||||
|
||||
const { sdkVersion } = cmd;
|
||||
await setup(platform, sdkVersion);
|
||||
l.info('it\'s all set!')
|
||||
l.info('it\'s all set!');
|
||||
} catch (err) {
|
||||
logger.error(`Failed to setup environment for ${platform} builds`);
|
||||
logger.error(err.stack);
|
||||
|
||||
+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',
|
||||
|
||||
+18
-14
@@ -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 logger from 'turtle/logger';
|
||||
import * as commands from 'turtle/bin/commands';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
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,13 +17,15 @@ Command.prototype.asyncAction = function asyncAction(asyncFn: (...asyncFnArgs: a
|
||||
return this.action(async (...args) => {
|
||||
try {
|
||||
await checkForUpdateAsync();
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
logger.warn('Failed to check for turtle-cli update.');
|
||||
}
|
||||
return await asyncFn(...args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function run(programName: string) {
|
||||
runAsync(programName).catch(e => {
|
||||
runAsync(programName).catch((e) => {
|
||||
logger.error('Uncaught Error', e);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -29,22 +33,22 @@ export function run(programName: string) {
|
||||
|
||||
async function runAsync(programName: string) {
|
||||
program.version(version);
|
||||
Object.values(commands).forEach(command => registerCommand(program, command));
|
||||
Object.values(commands).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,20 @@ 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';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
const which = util.promisify(_which);
|
||||
const REQUIRED_TOOLS: Array<IToolDefinition> = [
|
||||
const REQUIRED_TOOLS: IToolDefinition[] = [
|
||||
{
|
||||
command: 'bash',
|
||||
missingDescription: 'Please install bash',
|
||||
@@ -31,13 +31,13 @@ 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' };
|
||||
@@ -50,7 +50,7 @@ export default async function setup(sdkVersion?: string) {
|
||||
await ensureShellAppIsPresent(
|
||||
sdkVersion,
|
||||
{ formatShellAppDirectory, formatShellAppTarballUriPath },
|
||||
_shellAppPostDownloadAction
|
||||
_shellAppPostDownloadAction,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,10 +4,10 @@ import path from 'path';
|
||||
import decompress from 'decompress';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { formatArtifactDownloadPath } from 'turtle/bin/setup/utils/common';
|
||||
import download from 'turtle/bin/setup/utils/downloader';
|
||||
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';
|
||||
|
||||
const ANDROID_NDK_URL = `https://dl.google.com/android/repository/android-ndk-r10e-${os.platform()}-x86_64.zip`;
|
||||
|
||||
@@ -24,7 +24,7 @@ export default async function ensureAndroidNDKIsPresent() {
|
||||
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 +47,5 @@ function _createEnvVars(androidNdkDir: string) {
|
||||
}
|
||||
|
||||
function _createPaths(androidNdkDir: string) {
|
||||
return [androidNdkDir]
|
||||
return [androidNdkDir];
|
||||
}
|
||||
@@ -5,12 +5,12 @@ import decompress from 'decompress';
|
||||
import fs from 'fs-extra';
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import { formatArtifactDownloadPath } from 'turtle/bin/setup/utils/common';
|
||||
import download from 'turtle/bin/setup/utils/downloader';
|
||||
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';
|
||||
|
||||
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);
|
||||
@@ -26,7 +26,7 @@ export default async function ensureAndroidSDKIsPresent() {
|
||||
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 +73,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,13 +24,13 @@ 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;
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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) {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
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';
|
||||
import { ensureToolsAreInstalled, IToolDefinition } from 'turtle/bin/setup/utils/toolsDetector';
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
const l = logger.withFields({ buildPhase: 'setting up environment' });
|
||||
|
||||
@@ -19,11 +18,15 @@ interface IShellAppFormaters {
|
||||
|
||||
type PostDownloadAction = (workingdir: string) => void;
|
||||
|
||||
export async function checkSystem(requiredTools: Array<IToolDefinition>) {
|
||||
export async function checkSystem(requiredTools: IToolDefinition[]) {
|
||||
await ensureToolsAreInstalled(requiredTools);
|
||||
}
|
||||
|
||||
export async function ensureShellAppIsPresent(sdkVersion: string, formatters: IShellAppFormaters, postDownloadAction?: PostDownloadAction) {
|
||||
export async function ensureShellAppIsPresent(
|
||||
sdkVersion: string,
|
||||
formatters: IShellAppFormaters,
|
||||
postDownloadAction?: PostDownloadAction,
|
||||
) {
|
||||
const workingdir = formatters.formatShellAppDirectory(sdkVersion);
|
||||
if (await fs.pathExists(workingdir)) {
|
||||
return;
|
||||
@@ -45,12 +48,11 @@ async function _downloadShellApp(sdkVersion: string, targetDirectory: string, fo
|
||||
l.info('extracting shell app (this may take a while)...');
|
||||
await tar.x({
|
||||
file: tarballDownloadTargetPath,
|
||||
C: targetDirectory
|
||||
C: targetDirectory,
|
||||
});
|
||||
l.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) {
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface IToolDefinition {
|
||||
testFn?: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
export async function ensureToolsAreInstalled(tools: Array<IToolDefinition>) {
|
||||
export async function ensureToolsAreInstalled(tools: IToolDefinition[]) {
|
||||
let isAnyToolMissing = false;
|
||||
for (const { command, missingDescription, testFn } of tools) {
|
||||
try {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
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 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 setup from 'turtle/bin/setup/setup';
|
||||
|
||||
export function createBuilderAction({
|
||||
program,
|
||||
@@ -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,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
export async function loadAppJSON(projectDirArg: string) {
|
||||
const projectDir = resolveAbsoluteDir(projectDirArg);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,17 +2,17 @@ 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 config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import { IAndroidCredentials, IJob, IJobResult } from 'turtle/job';
|
||||
import { formatShellAppDirectory } from 'turtle/builders/utils/android/workingdir';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
export default async function buildAndroid(jobData: IJob): Promise<IJobResult> {
|
||||
const credentials = await getOrCreateCredentials(jobData);
|
||||
@@ -25,7 +25,10 @@ export default async function buildAndroid(jobData: IJob): Promise<IJobResult> {
|
||||
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),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import buildIOS from './ios';
|
||||
import buildAndroid from './android';
|
||||
import buildIOS from './ios';
|
||||
|
||||
export default {
|
||||
ios: buildIOS,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
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';
|
||||
import logger from 'turtle/logger/index';
|
||||
|
||||
export default async function buildArchive(ctx: IContext, job: IJob) {
|
||||
let keychainInfo;
|
||||
@@ -34,11 +34,11 @@ async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, m
|
||||
|
||||
const {
|
||||
credentials: { provisioningProfile, certPassword, teamId, password },
|
||||
config: { bundleIdentifier: _bundleIdentifierFromConfig },
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
@@ -30,9 +30,15 @@ const { BUILD_TYPES } = IOS;
|
||||
export function createBuilderContext(job: IJob): 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 sdkVersion = sdkVersionFromJob || sdkVersionFromManifest;
|
||||
const workingDir = formatShellAppDirectory(sdkVersion);
|
||||
|
||||
const context: any = {
|
||||
@@ -44,7 +50,7 @@ export function createBuilderContext(job: IJob): IContext {
|
||||
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');
|
||||
@@ -55,7 +61,7 @@ export function createBuilderContext(job: IJob): IContext {
|
||||
context.archiveDir = join(
|
||||
context.baseArchiveDir,
|
||||
'Release',
|
||||
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.app`
|
||||
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.app`,
|
||||
);
|
||||
} else if (buildType === BUILD_TYPES.ARCHIVE) {
|
||||
context.outputPath = join(context.appDir, 'archive.xcarchive');
|
||||
@@ -66,7 +72,7 @@ export function createBuilderContext(job: IJob): IContext {
|
||||
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.xcarchive`,
|
||||
'Products',
|
||||
'Applications',
|
||||
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.app`
|
||||
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.app`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,7 +80,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);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
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';
|
||||
@@ -15,7 +15,7 @@ import { IJob, IJobResult } from 'turtle/job';
|
||||
|
||||
const { BUILD_TYPES } = IOS;
|
||||
|
||||
let SUPPORTED_SDK_VERSIONS: Array<number>;
|
||||
let SUPPORTED_SDK_VERSIONS: number[];
|
||||
|
||||
export default async function iosBuilder(job: IJob): Promise<IJobResult> {
|
||||
await ensureCanBuildSdkVersion(job);
|
||||
@@ -59,7 +59,7 @@ async function cleanup(ctx: IContext) {
|
||||
}
|
||||
|
||||
const getTemporaryDirs = (ctx: IContext) =>
|
||||
Object.values(_.pick(ctx, ['appDir', 'provisioningProfileDir']));
|
||||
Object.values(pick(ctx, ['appDir', 'provisioningProfileDir']));
|
||||
|
||||
async function ensureCanBuildSdkVersion(job: IJob) {
|
||||
if (config.builder.useLocalWorkingDir) {
|
||||
@@ -71,14 +71,14 @@ async function ensureCanBuildSdkVersion(job: IJob) {
|
||||
}
|
||||
|
||||
const targetMajorSdkVersion = ExponentTools.parseSdkMajorVersion(job.sdkVersion || job.manifest.sdkVersion);
|
||||
if (!_.includes(SUPPORTED_SDK_VERSIONS, targetMajorSdkVersion)) {
|
||||
if (!SUPPORTED_SDK_VERSIONS.includes(targetMajorSdkVersion)) {
|
||||
throw new Error(`Unsupported SDK Version!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function findSupportedSdkVersions(): Promise<Array<number>> {
|
||||
async function findSupportedSdkVersions(): Promise<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)));
|
||||
const sdks = files.filter((file) => file.startsWith(SDK_DIR_PREFIX));
|
||||
return sdks.map((sdk) => parseInt(sdk.substr(SDK_DIR_PREFIX.length), 10));
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
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';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
export default async function buildSimulator(ctx: IContext, job: IJob) {
|
||||
const l = logger.withFields({ buildPhase: 'running simulator builder' });
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import path from 'path';
|
||||
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import fs from 'fs-extra';
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import { AndroidKeystore } from 'xdl';
|
||||
|
||||
import * as sqs from 'turtle/aws/sqs';
|
||||
import { UPDATE_CREDENTIALS } from 'turtle/constants/build';
|
||||
import { IAndroidCredentials, IJob } from 'turtle/job';
|
||||
import logger from 'turtle/logger';
|
||||
import { isOffline } from 'turtle/turtleContext';
|
||||
import { IAndroidCredentials, IJob } from 'turtle/job';
|
||||
|
||||
async function getOrCreateCredentials(jobData: IJob): Promise<IAndroidCredentials> {
|
||||
if (!jobData.credentials) {
|
||||
@@ -42,7 +42,6 @@ async function getOrCreateCredentials(jobData: IJob): Promise<IAndroidCredential
|
||||
} else {
|
||||
await sqs.sendMessage(jobData.id, UPDATE_CREDENTIALS, {
|
||||
...credentials,
|
||||
turtleVersion: jobData.config.turtleVersion,
|
||||
});
|
||||
l.info('Keystore sent to storage successfully');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import path from 'path';
|
||||
import config from 'turtle/config';
|
||||
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
export function formatShellAppDirectory(sdkVersion: string) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as url from 'url';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import { IJob } from 'turtle/job';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
export function getExperienceUrl(job: IJob) {
|
||||
const { experienceName, config: jobConfig } = job;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { IosKeychain } from 'xdl';
|
||||
|
||||
import { IContext } from 'turtle/builders/ios/context';
|
||||
import * as fileUtils from 'turtle/builders/utils/file';
|
||||
import logger from 'turtle/logger';
|
||||
import { IContext } from 'turtle/builders/ios/context';
|
||||
|
||||
interface IKeychain {
|
||||
path: string;
|
||||
}
|
||||
|
||||
|
||||
export async function create(ctx: IContext): Promise<IKeychain> {
|
||||
const l = logger.withFields({ buildPhase: 'creating keychain' });
|
||||
try {
|
||||
|
||||
@@ -2,22 +2,22 @@ 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 { IJob } from 'turtle/job';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
import { IJob } from 'turtle/job';
|
||||
import logger from 'turtle/logger/index';
|
||||
|
||||
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 sdkVersion = _.get(manifest, 'sdkVersion', sdkVersionFromJob);
|
||||
const applicationFilesSrc = path.join(
|
||||
ctx.workingDir,
|
||||
'shellAppBase-builds',
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import path from 'path';
|
||||
|
||||
import { ExponentTools } from 'xdl';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
|
||||
+10
-4
@@ -1,8 +1,8 @@
|
||||
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'),
|
||||
@@ -66,7 +66,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 +77,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';
|
||||
|
||||
|
||||
@@ -7,9 +7,3 @@ export const PLATFORMS = {
|
||||
};
|
||||
|
||||
export { IOS, BUILD };
|
||||
|
||||
export const TURTLE_VERSIONS = {
|
||||
NEW: 'new',
|
||||
OLD: 'old',
|
||||
BOTH: 'both',
|
||||
};
|
||||
@@ -6,7 +6,6 @@ export interface IJob {
|
||||
sdkVersion: string;
|
||||
config: {
|
||||
releaseChannel: string;
|
||||
turtleVersion?: string;
|
||||
publicUrl?: string;
|
||||
// ios
|
||||
buildType?: string;
|
||||
|
||||
+29
-20
@@ -1,17 +1,22 @@
|
||||
import path from 'path';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import _ from 'lodash';
|
||||
import { logErrorOnce } from 'turtle/builders/utils/common';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
import * as sqs from 'turtle/aws/sqs';
|
||||
import { BUILD } from 'turtle/constants/index';
|
||||
import { sanitizeJob } from 'turtle/validator';
|
||||
import builders from 'turtle/builders';
|
||||
import { logErrorOnce } from 'turtle/builders/utils/common';
|
||||
import config from 'turtle/config';
|
||||
import * as redis from 'turtle/utils/redis';
|
||||
import { checkShouldExit, setCurrentJobId } from 'turtle/turtleContext';
|
||||
import * as buildStatusMetric from 'turtle/metrics/buildStatus';
|
||||
import { BUILD } from 'turtle/constants/index';
|
||||
import logger from 'turtle/logger';
|
||||
import * as buildDurationMetric from 'turtle/metrics/buildDuration';
|
||||
import * as buildStatusMetric from 'turtle/metrics/buildStatus';
|
||||
import { checkShouldExit, setCurrentJobId } from 'turtle/turtleContext';
|
||||
import { getPriorities } from 'turtle/utils/priorities';
|
||||
import * as redis from 'turtle/utils/redis';
|
||||
import { sanitizeJob } from 'turtle/validator';
|
||||
|
||||
const { version: turtleVersion } = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
||||
|
||||
function _maybeExit() {
|
||||
if (checkShouldExit()) {
|
||||
@@ -29,7 +34,6 @@ export async function doJob() {
|
||||
export async function getJob() {
|
||||
_maybeExit();
|
||||
logger.info('Fetching job');
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
try {
|
||||
let job = null;
|
||||
@@ -71,20 +75,21 @@ async function processJob(jobData: any) {
|
||||
logger.info('The job has been cancelled');
|
||||
} else {
|
||||
redis.registerListener(job.id, () => deleteMessage(priority, receiptHandle));
|
||||
const startTimestamp = +new Date();
|
||||
const startTimestamp = Date.now();
|
||||
let buildFailed = false;
|
||||
const buildType = _.get(job, 'config.buildType', 'default');
|
||||
try {
|
||||
const success = await build(job);
|
||||
buildStatusMetric.add(buildType, success);
|
||||
buildFailed = !success;
|
||||
const status = await build(job);
|
||||
buildStatusMetric.add(buildType, status);
|
||||
buildFailed = !status;
|
||||
} catch (err) {
|
||||
buildStatusMetric.add(buildType, false);
|
||||
buildFailed = true;
|
||||
throw err;
|
||||
} finally {
|
||||
const endTimestamp = +new Date();
|
||||
const endTimestamp = Date.now();
|
||||
const turtleBuildDurationSecs = Math.ceil((endTimestamp - startTimestamp) / 1000);
|
||||
logger.info(`BuildID=${job.id} Build duration=${turtleBuildDurationSecs}`);
|
||||
buildDurationMetric.addTurtleDuration(buildType, turtleBuildDurationSecs, !buildFailed);
|
||||
if (job.messageCreatedTimestamp) {
|
||||
const totalBuildDurationSecs = Math.ceil(
|
||||
@@ -109,9 +114,7 @@ async function processJob(jobData: any) {
|
||||
function failAfterMaxJobTime(priority: string, receiptHandle: string, job: any) {
|
||||
return setTimeout(async () => {
|
||||
try {
|
||||
sqs.sendMessage(job.id, BUILD.JOB_STATES.ERRORED, {
|
||||
turtleVersion: job.config.turtleVersion,
|
||||
});
|
||||
sqs.sendMessage(job.id, BUILD.JOB_STATES.ERRORED, { turtleVersion });
|
||||
await deleteMessage(priority, receiptHandle);
|
||||
} finally {
|
||||
logger.error('Going to terminate turtle agent, just in case...');
|
||||
@@ -121,21 +124,27 @@ function failAfterMaxJobTime(priority: string, receiptHandle: string, job: any)
|
||||
}
|
||||
|
||||
async function build(job: any) {
|
||||
const startTimestamp = Date.now();
|
||||
const s3Url = await logger.init(job);
|
||||
const { turtleVersion } = job.config;
|
||||
const calculateBuildDuration = () => Math.ceil((Date.now() - startTimestamp) / 1000);
|
||||
|
||||
try {
|
||||
await sqs.sendMessage(job.id, BUILD.JOB_STATES.IN_PROGRESS, {
|
||||
logUrl: s3Url,
|
||||
logFormat: 'json',
|
||||
turtleVersion,
|
||||
});
|
||||
const result = await (builders as any)[job.platform](job);
|
||||
sqs.sendMessage(job.id, BUILD.JOB_STATES.FINISHED, { ...result, turtleVersion });
|
||||
const buildDuration = calculateBuildDuration();
|
||||
sqs.sendMessage(job.id, BUILD.JOB_STATES.FINISHED, {
|
||||
...result,
|
||||
turtleVersion,
|
||||
buildDuration,
|
||||
});
|
||||
return true;
|
||||
} catch (err) {
|
||||
logErrorOnce(err);
|
||||
sqs.sendMessage(job.id, BUILD.JOB_STATES.ERRORED, { turtleVersion });
|
||||
const buildDuration = calculateBuildDuration();
|
||||
sqs.sendMessage(job.id, BUILD.JOB_STATES.ERRORED, { turtleVersion, buildDuration });
|
||||
return false;
|
||||
} finally {
|
||||
await logger.cleanup(job);
|
||||
|
||||
@@ -12,5 +12,5 @@ export default baseJobSchema.concat(
|
||||
keystoreAlias: Joi.string(),
|
||||
keystore: Joi.string(),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
);
|
||||
@@ -1,6 +1,6 @@
|
||||
import Joi from 'joi';
|
||||
|
||||
import { PLATFORMS, TURTLE_VERSIONS } from 'turtle/constants';
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
|
||||
export default Joi.object().keys({
|
||||
platform: Joi.string().valid(Object.values(PLATFORMS)),
|
||||
@@ -10,7 +10,6 @@ export default Joi.object().keys({
|
||||
config: Joi.object()
|
||||
.keys({
|
||||
releaseChannel: Joi.string().regex(/[a-z\d][a-z\d._-]*/),
|
||||
turtleVersion: Joi.string().valid(Object.values(TURTLE_VERSIONS)),
|
||||
})
|
||||
.unknown(true),
|
||||
projectDir: Joi.string(),
|
||||
|
||||
@@ -23,5 +23,5 @@ export default baseJobSchema.concat(
|
||||
then: Joi.optional(),
|
||||
otherwise: Joi.required(),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
);
|
||||
@@ -6,36 +6,36 @@ import config from 'turtle/config';
|
||||
import * as constants from 'turtle/constants/logger';
|
||||
import { IJob } from 'turtle/job';
|
||||
|
||||
export interface LogglyConfig {
|
||||
export interface ILogglyConfig {
|
||||
token: string;
|
||||
subdomain: string;
|
||||
tags: Array<string>;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface LogglyExtraFields {
|
||||
jobID: string,
|
||||
interface ILogglyExtraFields {
|
||||
jobID: string;
|
||||
experienceName: string;
|
||||
}
|
||||
|
||||
export default class HackyLogglyStream extends EventEmitter {
|
||||
logglyStream: BunyanLoggly;
|
||||
extraFields: LogglyExtraFields | null;
|
||||
private logglyStream: BunyanLoggly;
|
||||
private extraFields: ILogglyExtraFields | null;
|
||||
|
||||
constructor(logglyConfig: LogglyConfig) {
|
||||
constructor(logglyConfig: ILogglyConfig) {
|
||||
super();
|
||||
this.logglyStream = new BunyanLoggly(logglyConfig, config.logger.loggly.buffer);
|
||||
this.extraFields = null;
|
||||
}
|
||||
init(job: IJob) {
|
||||
public init(job: IJob) {
|
||||
this.extraFields = {
|
||||
jobID: job.id,
|
||||
experienceName: job.experienceName
|
||||
experienceName: job.experienceName,
|
||||
};
|
||||
}
|
||||
cleanup() {
|
||||
public cleanup() {
|
||||
this.extraFields = null;
|
||||
}
|
||||
write(rec: any) {
|
||||
public write(rec: any) {
|
||||
const { msg: message, hostname: host, level, ...other } = rec;
|
||||
this.logglyStream.write({
|
||||
message,
|
||||
|
||||
+11
-11
@@ -1,16 +1,16 @@
|
||||
import bunyan from 'bunyan';
|
||||
import { Client as RavenClient } from 'raven';
|
||||
import { SentryStream } from 'bunyan-sentry-stream';
|
||||
import bunyanDebugStream from 'bunyan-debug-stream';
|
||||
import { SentryStream } from 'bunyan-sentry-stream';
|
||||
import { Client as RavenClient } from 'raven';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import S3Stream from 'turtle/logger/s3Stream';
|
||||
import HackyLogglyStream from 'turtle/logger/hackyLogglyStream';
|
||||
import * as constants from 'turtle/constants/logger';
|
||||
import { isOffline } from 'turtle/turtleContext';
|
||||
import { IJob } from 'turtle/job';
|
||||
import HackyLogglyStream from 'turtle/logger/hackyLogglyStream';
|
||||
import S3Stream from 'turtle/logger/s3Stream';
|
||||
import { isOffline } from 'turtle/turtleContext';
|
||||
|
||||
interface Stream {
|
||||
interface IStream {
|
||||
stream: any;
|
||||
type?: string;
|
||||
level?: string;
|
||||
@@ -20,7 +20,7 @@ interface Stream {
|
||||
export const s3logger = new S3Stream();
|
||||
let logglyStream: HackyLogglyStream;
|
||||
|
||||
const streams: Stream[] = [];
|
||||
const streams: IStream[] = [];
|
||||
|
||||
if (isOffline()) {
|
||||
const prettyStdOut = new bunyanDebugStream({ forceColor: true });
|
||||
@@ -33,7 +33,7 @@ if (isOffline()) {
|
||||
stream: s3logger,
|
||||
reemitErrorEvents: true,
|
||||
level: config.logger.client.level,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ if (config.logger.loggly.token) {
|
||||
'app-shell-apps',
|
||||
`platform.${config.platform}`,
|
||||
`environment.${config.deploymentEnv}`,
|
||||
]
|
||||
],
|
||||
};
|
||||
logglyStream = new HackyLogglyStream(logglyConfig);
|
||||
streams.push({
|
||||
@@ -91,9 +91,9 @@ logger.cleanup = async () => {
|
||||
|
||||
export default logger;
|
||||
|
||||
export function withFields(_logger: any, extraFields: any) {
|
||||
export function withFields(loggerObj: any, extraFields: any) {
|
||||
return constants.LEVELS.reduce((obj, level) => {
|
||||
obj[level] = (...args: any[]) => _logger[level](extraFields, ...args);
|
||||
obj[level] = (...args: any[]) => loggerObj[level](extraFields, ...args);
|
||||
return obj;
|
||||
}, {} as any);
|
||||
}
|
||||
+24
-24
@@ -1,6 +1,6 @@
|
||||
|
||||
import path from 'path';
|
||||
import EventEmitter from 'events';
|
||||
import path from 'path';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import _ from 'lodash';
|
||||
@@ -11,13 +11,13 @@ import * as constants from 'turtle/constants/logger';
|
||||
import { IJob } from 'turtle/job';
|
||||
|
||||
export default class S3Stream extends EventEmitter {
|
||||
s3Url: string | null;
|
||||
lastFlush: number | null;
|
||||
filePath: string | null;
|
||||
fileHandle: number | null;
|
||||
finishedPromise: Promise<any> | null;
|
||||
finishedPromiseResolveFn: any;
|
||||
secrets: any;
|
||||
private s3Url: string | null;
|
||||
private lastFlush: number | null;
|
||||
private filePath: string | null;
|
||||
private fileHandle: number | null;
|
||||
private finishedPromise: Promise<any> | null;
|
||||
private finishedPromiseResolveFn: any;
|
||||
private secrets: any;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -29,7 +29,7 @@ export default class S3Stream extends EventEmitter {
|
||||
this.finishedPromiseResolveFn = null;
|
||||
}
|
||||
|
||||
async init(job: IJob) {
|
||||
public async init(job: IJob) {
|
||||
this.s3Url = `logs/${job.experienceName}/${job.id}`;
|
||||
const dir = config.directories.tempS3LogsDir;
|
||||
const exists = await fs.pathExists(dir);
|
||||
@@ -39,12 +39,12 @@ export default class S3Stream extends EventEmitter {
|
||||
this.filePath = path.join(dir, job.id);
|
||||
this.fileHandle = await fs.open(this.filePath, 'w+', 0o660);
|
||||
this.secrets = {};
|
||||
this.finishedPromise = new Promise(res => (this.finishedPromiseResolveFn = res));
|
||||
this.finishedPromise = new Promise((resolveFn) => (this.finishedPromiseResolveFn = resolveFn));
|
||||
const res = await this.flush() as any;
|
||||
return res.Location;
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
public async cleanup() {
|
||||
const res = this.finishedPromiseResolveFn;
|
||||
try {
|
||||
await this.flush(true);
|
||||
@@ -59,7 +59,7 @@ export default class S3Stream extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
flush(force = false) {
|
||||
public flush(force = false) {
|
||||
if (force || !this.lastFlush) {
|
||||
return this._flush();
|
||||
}
|
||||
@@ -69,15 +69,7 @@ export default class S3Stream extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
_flush() {
|
||||
this.lastFlush = Date.now();
|
||||
return uploadFile({
|
||||
srcPath: this.filePath as string,
|
||||
key: this.s3Url as string,
|
||||
});
|
||||
}
|
||||
|
||||
write(recRaw: any) {
|
||||
public write(recRaw: any) {
|
||||
if (this.fileHandle) {
|
||||
const rec = JSON.stringify({
|
||||
...recRaw,
|
||||
@@ -88,7 +80,7 @@ export default class S3Stream extends EventEmitter {
|
||||
}
|
||||
const recSanitized = Object.keys(this.secrets).reduce(
|
||||
(acc, secret) => acc.replace(new RegExp(secret, 'g'), this.secrets[secret]),
|
||||
rec
|
||||
rec,
|
||||
);
|
||||
fs
|
||||
.write(this.fileHandle as number, `${recSanitized}\n`)
|
||||
@@ -98,14 +90,22 @@ export default class S3Stream extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
addSecretsToFilter(secrets: any) {
|
||||
public addSecretsToFilter(secrets: any) {
|
||||
const filteredSecrets = secrets.filter((i: any) => i !== undefined && i !== null);
|
||||
const replacements = filteredSecrets.map((i: any) => 'X'.repeat(i.length));
|
||||
const toAdd = _.zipObject(filteredSecrets, replacements);
|
||||
this.secrets = Object.assign({}, this.secrets, toAdd);
|
||||
}
|
||||
|
||||
waitForLogger() {
|
||||
public waitForLogger() {
|
||||
return this.finishedPromise;
|
||||
}
|
||||
|
||||
private _flush() {
|
||||
this.lastFlush = Date.now();
|
||||
return uploadFile({
|
||||
srcPath: this.filePath as string,
|
||||
key: this.s3Url as string,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import logger from 'turtle/logger';
|
||||
import * as cloudwatch from 'turtle/aws/cloudwatch';
|
||||
import { TYPE_DIMENSIONS } from 'turtle/aws/cloudwatch/dimensions';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
const TURTLE_BUILD_DURATION_METRIC_NAME = 'turtleBuildDuration';
|
||||
const TOTAL_BUILD_DURATION_METRIC_NAME = 'totalBuildDuration';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as cloudwatch from 'turtle/aws/cloudwatch';
|
||||
import { TYPE_DIMENSIONS } from 'turtle/aws/cloudwatch/dimensions';
|
||||
import * as reducers from 'turtle/aws/cloudwatch/reducers';
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import * as cloudwatch from 'turtle/aws/cloudwatch';
|
||||
import * as reducers from 'turtle/aws/cloudwatch/reducers';
|
||||
import { TYPE_DIMENSIONS } from 'turtle/aws/cloudwatch/dimensions';
|
||||
|
||||
const BUILD_METRIC_NAME = 'build';
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import logger from 'turtle/logger';
|
||||
import * as cloudwatch from 'turtle/aws/cloudwatch';
|
||||
import * as buildStatus from 'turtle/metrics/buildStatus';
|
||||
import logger from 'turtle/logger';
|
||||
import * as buildDuration from 'turtle/metrics/buildDuration';
|
||||
import * as buildStatus from 'turtle/metrics/buildStatus';
|
||||
|
||||
const metricsToRegister = [buildStatus, buildDuration];
|
||||
|
||||
export async function init() {
|
||||
logger.info('Initializing Cloudwatch metrics');
|
||||
await cloudwatch.init();
|
||||
metricsToRegister.forEach(metric => metric.register());
|
||||
metricsToRegister.forEach((metric) => metric.register());
|
||||
}
|
||||
+5
-5
@@ -1,13 +1,13 @@
|
||||
export = BunyanLoggly
|
||||
export = BunyanLoggly;
|
||||
|
||||
declare class BunyanLoggly {
|
||||
constructor(options: BunyanLoggly.IOptions, bufferLength?: number, bufferTimeout?: number, logglyCallback?: Function);
|
||||
write(data: any): void;
|
||||
constructor(options: BunyanLoggly.IOptions, bufferLength?: number, bufferTimeout?: number, logglyCallback?: any);
|
||||
public write(data: any): void;
|
||||
}
|
||||
|
||||
declare namespace BunyanLoggly {
|
||||
interface IOptions {
|
||||
token: string
|
||||
subdomain: string
|
||||
token: string;
|
||||
subdomain: string;
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -1,12 +1,12 @@
|
||||
import { LoggerDetach } from 'xdl';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import { doJob } from 'turtle/jobManager';
|
||||
import logger from 'turtle/logger';
|
||||
import setup from 'turtle/setup';
|
||||
import { doJob } from 'turtle/jobManager';
|
||||
import { setShouldExit, checkShouldExit } from 'turtle/turtleContext';
|
||||
import { checkShouldExit, setShouldExit } from 'turtle/turtleContext';
|
||||
|
||||
process.on('unhandledRejection', err => logger.error('Unhandled promise rejection:', err));
|
||||
process.on('unhandledRejection', (err) => logger.error('Unhandled promise rejection:', err));
|
||||
|
||||
function handleExit() {
|
||||
if (checkShouldExit()) {
|
||||
@@ -14,7 +14,7 @@ function handleExit() {
|
||||
process.exit(1);
|
||||
}
|
||||
logger.warn(
|
||||
`Received termination signal. Will exit after current build. To force exit press Ctrl-C again`
|
||||
`Received termination signal. Will exit after current build. To force exit press Ctrl-C again`,
|
||||
);
|
||||
setShouldExit();
|
||||
}
|
||||
@@ -23,13 +23,13 @@ process.on('SIGINT', handleExit);
|
||||
|
||||
async function main() {
|
||||
logger.info(
|
||||
`Starting Turtle... (NODE_ENV=${config.env}, PLATFORM=${config.platform}, DEPLOYMENT_ENVIRONMENT=${config.deploymentEnv})`
|
||||
'Starting Turtle... '
|
||||
+ `NODE_ENV=${config.env}, PLATFORM=${config.platform}, DEPLOYMENT_ENVIRONMENT=${config.deploymentEnv}`,
|
||||
);
|
||||
LoggerDetach.configure(logger);
|
||||
if (setup[config.platform]) {
|
||||
await setup[config.platform]();
|
||||
}
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
try {
|
||||
await doJob();
|
||||
@@ -41,5 +41,5 @@ async function main() {
|
||||
|
||||
main()
|
||||
.then(() => logger.error('This should never happen...'))
|
||||
.catch(err => logger.error('Something went terribly wrong :(', err))
|
||||
.catch((err) => logger.error('Something went terribly wrong :(', err))
|
||||
.then(() => process.exit(1));
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { PLATFORMS } from 'turtle/constants';
|
||||
import ios from 'turtle/setup/ios';
|
||||
import android from 'turtle/setup/android';
|
||||
import ios from 'turtle/setup/ios';
|
||||
|
||||
export default {
|
||||
[PLATFORMS.IOS]: ios,
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import fs from 'fs-extra';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import * as keychain from 'turtle/builders/utils/ios/keychain';
|
||||
import logger from 'turtle/logger';
|
||||
@@ -11,9 +11,9 @@ async function deleteProvisioningProfilesFromHomedir() {
|
||||
const exists = await fs.pathExists(provisioningProfilesDir);
|
||||
if (exists) {
|
||||
const provisioningProfiles = (await fs.readdir(provisioningProfilesDir)).filter(
|
||||
filename => path.extname(filename) === '.mobileprovision',
|
||||
(filename) => path.extname(filename) === '.mobileprovision',
|
||||
);
|
||||
await Promise.all(provisioningProfiles.map(file => fs.remove(path.join(provisioningProfilesDir, file))));
|
||||
await Promise.all(provisioningProfiles.map((file) => fs.remove(path.join(provisioningProfilesDir, file))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import config from 'turtle/config';
|
||||
|
||||
let shouldExit = false;
|
||||
let _currentJobId: string | null = null;
|
||||
let currentJobId: string | null = null;
|
||||
|
||||
export const setShouldExit = () => {
|
||||
shouldExit = true;
|
||||
@@ -12,11 +12,11 @@ export const checkShouldExit = () => {
|
||||
};
|
||||
|
||||
export const getCurrentJobId = () => {
|
||||
return _currentJobId;
|
||||
return currentJobId;
|
||||
};
|
||||
|
||||
export const setCurrentJobId = (id: string | null) => {
|
||||
_currentJobId = id;
|
||||
currentJobId = id;
|
||||
};
|
||||
|
||||
export const isOffline = () => config.builder.mode === 'offline';
|
||||
@@ -1,7 +1,8 @@
|
||||
import config from 'turtle/config';
|
||||
import os from 'os';
|
||||
import logger from 'turtle/logger';
|
||||
import Joi from 'joi';
|
||||
import os from 'os';
|
||||
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
import { getRedisClient, RedisClient } from 'turtle/utils/redis';
|
||||
|
||||
export const NORMAL = 'normalPriority';
|
||||
|
||||
+8
-9
@@ -1,21 +1,20 @@
|
||||
import Redis from 'ioredis';
|
||||
|
||||
import logger from 'turtle/logger';
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
const MILLIS_TO_UPLOAD_LOGS = 3000;
|
||||
export const MILLIS_CONNECTION_TIMEOUT = 10000;
|
||||
|
||||
|
||||
export enum RedisClient {
|
||||
Subscriber = 'REDIS_CLIENT_SUBSCRIBER',
|
||||
Default = 'REDIS_CLIENT_DEFAULT',
|
||||
Configuration = 'REDIS_CLIENT_CONFIGURATION',
|
||||
}
|
||||
|
||||
type ClientConfig = { [key: string]: string; };
|
||||
interface IClientConfig { [key: string]: string; }
|
||||
|
||||
const clientToUrl: ClientConfig = {
|
||||
const clientToUrl: IClientConfig = {
|
||||
[RedisClient.Subscriber]: config.redis.url,
|
||||
[RedisClient.Default]: config.redis.url,
|
||||
[RedisClient.Configuration]: config.redis.configUrl,
|
||||
@@ -42,21 +41,21 @@ interface IRedisClients {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const _redisClients: IRedisClients = {
|
||||
const redisClients: IRedisClients = {
|
||||
[RedisClient.Subscriber]: null,
|
||||
[RedisClient.Default]: null,
|
||||
[RedisClient.Configuration]: null,
|
||||
};
|
||||
|
||||
export async function getRedisClient(type = RedisClient.Default) {
|
||||
if (!_redisClients[type]) {
|
||||
if (!redisClients[type]) {
|
||||
try {
|
||||
_redisClients[type] = await connect(MILLIS_CONNECTION_TIMEOUT, type);
|
||||
redisClients[type] = await connect(MILLIS_CONNECTION_TIMEOUT, type);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
return _redisClients[type];
|
||||
return redisClients[type];
|
||||
}
|
||||
|
||||
export async function checkIfCancelled(jobId: string) {
|
||||
@@ -76,7 +75,7 @@ export async function registerListener(jobId: string, deleteMessage: any) {
|
||||
try {
|
||||
const redis = await getRedisClient(RedisClient.Subscriber);
|
||||
redis.subscribe('jobs:cancelled');
|
||||
redis.on('message', async function(_: any, message: any) {
|
||||
redis.on('message', async (_: any, message: any) => {
|
||||
if (message === jobId) {
|
||||
logger.info({ lastBuildLog: true }, 'Job cancelled - killing process');
|
||||
await deleteMessage();
|
||||
|
||||
@@ -8,4 +8,9 @@ if [ ! -f "$KEYFILE" ]; then
|
||||
update-local-secrets --keyfile-only
|
||||
fi
|
||||
|
||||
if ! command -v git-crypt >/dev/null 2>&1 ; then
|
||||
echo "You need to have git-crypt installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git-crypt unlock "$KEYFILE"
|
||||
+2
-1
@@ -4,7 +4,8 @@
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"quotemark": [true, "single", "jsx-double"],
|
||||
"object-literal-sort-keys": false
|
||||
"object-literal-sort-keys": false,
|
||||
"no-namespace": false
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
Reference in new issue
Block a user