Compare commits

...
Author SHA1 Message Date
Dominik Sokal e8466f4ccc add .nvmrc 2019-04-12 09:55:06 +02:00
Dominik Sokal 64331d6a19 don't use node from nix locally 2019-04-12 09:46:30 +02:00
Eric Samelson c0cf273b7c update iOS shell app for SDK 32 to fix AppAuth issue 2019-04-10 12:53:22 -07:00
Eric Samelson cbb8faef12 update CHANGELOG for 0.6.0 2019-04-09 18:38:49 -07:00
Eric Samelson d24b82b5c2 Release 0.6.0 2019-04-09 18:37:19 -07:00
Eric Samelson 3f5ddf6a22 update all old android shell tarballs to remove Branch module 2019-04-08 17:34:40 -07:00
Eric Samelson 80f30bb29c update android SDK 32 tarball to 2.10.8 (removes Branch module) 2019-04-08 12:34:05 -07:00
Eric Samelson c6e2b07fff update CHANGELOG for 0.5.15 2019-04-03 15:59:56 -07:00
Eric Samelson 80b0029d57 Release 0.5.15 2019-04-03 15:58:05 -07:00
Eric Samelson 17bda59676 update ios SDK 32 shell tarball to 2.10.6 2019-04-03 14:28:42 -07:00
Dominik Sokal 2fbcbaa525 Fix lint errors before commit (#34)
Copy-pasted from expo-cli.
2019-04-03 11:37:44 +02:00
Dominik Sokal 22e47e4e43 update CHANGELOG 2019-04-03 10:15:11 +02:00
Dominik Sokal 1d4728e402 goodbye loggly (#32) 2019-04-03 09:53:01 +02:00
Nick Novitski de4837f361 Log error objects where possible (#33)
* Log error objects where possible

* fix lint errors
2019-04-03 09:50:54 +02:00
Dominik Sokal 20d393e48e add object-curly-spacing rule 2019-04-03 09:37:32 +02:00
Dominik Sokal 95714bdf5e update CHANGELOG 2019-04-02 15:36:15 +02:00
Dominik Sokal 8e2b593e3e only alter path if not in universe 2019-04-02 13:24:11 +02:00
Dominik Sokal 885cf08a9d fix fetchRemoteTarballs.sh 2019-04-01 15:46:46 +02:00
Dominik Sokal bd816e1124 make scripts/fetchRemoteTarballs.sh executable 2019-04-01 15:10:15 +02:00
Dominik Sokal 98549f147c don't use awscli for downloading shell app tarballs 2019-04-01 14:48:33 +02:00
Dominik Sokal 9de691c60e adds running smoke tests on CircleCI (#28)
Closes #24.

Turtle smoke tests are being executed from the context of `expo/universe` repository so far. That doesn't make too much sense. I moved running those tests to this repository.
2019-04-01 13:25:30 +02:00
Dominik Sokal 71e270dc50 don't install git in docker 2019-03-28 14:59:56 +01:00
Eric Samelson 26e684069d update CHANGELOG 2019-03-22 15:58:50 -07:00
Eric Samelson 1c67df516e Release 0.5.14 2019-03-22 15:56:25 -07:00
Eric Samelson d7ed489876 update ios sdk32 tarball to 2.10.5 2019-03-22 11:49:35 -07:00
Dominik Sokal 267e487494 change queues urls 2019-03-21 10:45:03 +01:00
Dominik Sokal b07d830eb9 replace ios recording file (#29) 2019-03-20 17:14:03 +01:00
Dominik Sokal bfaaf09e57 add config param (#27)
* add config param

* update CHANGELOG
2019-03-14 10:15:24 +01:00
Dominik Sokal 33b2a569aa Release 0.5.13 2019-03-12 15:10:40 +01:00
Dominik Sokal 2febf6bcdd upgrade xdl to 53.1.6-alpha.2 2019-03-12 15:08:41 +01:00
Wojciech Kozyra e2c9ee0e39 [android-builder] check if sdk is supported (#25) 2019-03-06 15:56:31 +01:00
Dominik Sokal f017540571 don't fetch android shell apps when running tests on ci (they are already there) 2019-02-28 11:55:26 +01:00
Dominik Sokal 5b65eb3d6e add json to moduleFileExtensions (jest) 2019-02-28 11:37:53 +01:00
Dominik Sokal 60874f31c9 Release 0.5.12 2019-02-26 09:51:28 +01:00
Dominik Sokal 78beb3c0b3 fix yarn.lock 2019-02-26 09:51:01 +01:00
Dominik Sokal 03633d94e5 update CHANGELOG 2019-02-26 09:46:17 +01:00
Nick Novitski 392e4487b7 Add google cloud logger (#21) 2019-02-25 13:10:11 -08:00
sierrakn f316cb0e0c Sending manifest when building Android shell app (#22)
# Why

Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests.

# How

How did you build this feature or fix this bug and why?

# Test Plan

Please describe how you tested this change and how a reviewer could reproduce your test, especially if this PR does not include automated tests! If possible, please also provide terminal output and/or screenshots demonstrating your test/reproduction.
2019-02-25 11:11:21 +01:00
Wojciech Kozyra 968ce7d8bb update traveling-fastlane 2019-02-22 14:56:10 +01:00
Wojciech Kozyra 5876a11568 Update CHANGELOG.md 2019-02-22 13:48:02 +01:00
Wojciech Kozyra 7448e1f4e4 Release 0.5.11 2019-02-22 13:45:28 +01:00
Dominik Sokal 4338e71005 [ios] add support for expo client builds (#19)
* [ios] add support for expo client builds

* change client shell app tarball url

* add creating an ad hoc provisioning profile

* cleanup logs

* update xdl and traveling-fastlane

* fix for breaking changes in xdl
2019-02-22 13:18:08 +01:00
Eric Samelson 7323412095 update CHANGELOG for 0.5.10 2019-02-19 14:07:32 -08:00
Eric Samelson c1d5a17e45 Release 0.5.10 2019-02-19 14:04:44 -08:00
Eric Samelson 2771ad0829 Revert "Android splash screen standalone app adjustments (#3)"
This reverts commit 31c17a4d72.
2019-02-19 11:29:00 -08:00
Eric Samelson 32cc988072 update android sdk 32 shell app tarball to 2.10.7 2019-02-19 11:28:01 -08:00
Wojciech Kozyra 0b3ce2c8e0 fix setup script 2019-02-14 10:26:11 +01:00
Wojciech Kozyra 9c46ee143e register turtle version in redis (#20)
register turtle version in redis
2019-02-13 15:53:03 +01:00
Bartłomiej Bukowski 31c17a4d72 Android splash screen standalone app adjustments (#3)
* Android splash screen standalone adjustments

* Update xdl
2019-02-06 14:49:43 +01:00
Stanisław Chmiela ee3f265294 Release 0.5.9 2019-02-06 14:02:31 +01:00
Stanisław Chmiela c1cca91e68 [changelog] Add changelog entry for 0.5.8 2019-02-06 14:01:45 +01:00
Stanisław Chmiela 038bafe20b [ios] Fix bare notifications in standalone mode on SDK32 (#18)
See https://github.com/expo/expo/pull/3431.
2019-02-06 13:58:01 +01:00
Eric Samelson f2fef264d7 update CHANGELOG for 0.5.7 2019-01-31 17:40:08 -08:00
Eric Samelson fae77b4455 Release 0.5.7 2019-01-31 17:36:57 -08:00
Eric Samelson a3ffa0411e update android sdk 32 shell app with bundled assets fix 2019-01-31 15:52:31 -08:00
Dominik Sokal 27dd680cbd update CHANGELOG 2019-01-25 13:35:12 +01:00
Dominik Sokal 2b5a370f48 Release 0.5.6 2019-01-25 13:30:00 +01:00
Dominik Sokal 21933f5a6a update shell apps for sdk 30, 31 and 32 (built with xcode 10.1) 2019-01-25 13:27:45 +01:00
Dominik Sokal a5bd4f0c97 Update CHANGELOG 2019-01-24 09:35:16 +01:00
Dominik Sokal 8e10aacf59 Release 0.5.5 2019-01-24 09:33:54 +01:00
Dominik Sokal 3dd74ef406 fix releaseBeta.sh 2019-01-23 17:00:49 +01:00
Dominik Sokal 87f7085fc4 print a reminder about updating CHANGELOG when releasing turtle-cli 2019-01-23 16:58:51 +01:00
Dominik Sokal 0b0b5a7c72 update CONTRIBUTING.md 2019-01-23 16:45:16 +01:00
Dominik Sokal d6dee79bcf update CHANGELOG 2019-01-23 16:45:09 +01:00
Dominik Sokal db05eeeed3 send build duration to www (#15) 2019-01-23 16:29:17 +01:00
89 changed files with 5100 additions and 3844 deletions

No files matched your search

+199 -12
View File
@@ -1,25 +1,212 @@
version: 2
version: 2.1
executors:
nix: &nix
docker:
- image: lnl7/nix:2.2
# Symlink to the readline-enabled bash which is the default command of the container
shell: /run/current-system/sw/bin/bash -leo pipefail
working_directory: ~/turtle
mac:
macos: # https://circleci.com/docs/2.0/testing-ios/#supported-xcode-versions
xcode: '10.1.0'
working_directory: /Users/distiller/project
shell: /bin/bash -leo pipefail
android:
<<: *nix
resource_class: xlarge
commands:
install_nix:
steps:
- run:
name: Install nix
command: curl https://nixos.org/releases/nix/nix-2.2.1/install | bash -s -- --no-daemon
- run:
name: Add nix-initializing line to .bashrc
command: echo ". ~/.nix-profile/etc/profile.d/nix.sh" >> ~/.bashrc
setup:
steps:
- run:
name: Install common packages
command: |
nix-env -iA \
nixpkgs.binutils \
nixpkgs.direnv \
nixpkgs.findutils \
nixpkgs.gawk \
nixpkgs.git \
nixpkgs.gnutar \
nixpkgs.gzip \
nixpkgs.openssh \
nixpkgs.python \
nixpkgs.xz
- run:
name: Configure direnv
command: |
mkdir -p ~/.config/direnv
echo -e "[whitelist]\nprefix = [ \"$HOME\" ]" > ~/.config/direnv/config.toml
echo 'eval "$(direnv export bash)"' >> ~/.bash_profile
- run:
name: Configure yarn
command: echo '--frozen-lockfile true' >> ~/.yarnrc
- checkout
- run:
name: Configure aws-cli
command: |
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
setup_nix:
steps:
- run:
name: Add cp and rm stubs
command: |
cat \<< 'EOF' >> /bin/cp
#! /usr/bin/env bash
/usr/bin/env cp "$@"
EOF
chmod +x /bin/cp
cat \<< 'EOF' >> /bin/rm
#! /usr/bin/env bash
/usr/bin/env rm "$@"
EOF
chmod +x /bin/rm
decrypt_secrets_if_possible:
steps:
- run:
name: Decrypt secrets if possible
command: |
if [[ -z "$TURTLE_GIT_CRYPT_KEY_BASE64" ]]; then
echo 'git-crypt key not present in environment'
else
git crypt unlock <(echo $TURTLE_GIT_CRYPT_KEY_BASE64 | base64 --decode)
fi
restore_yarn_cache:
parameters:
workingdir:
type: string
default: .
steps:
- restore_cache:
key: yarn-v1-{{ checksum "<< parameters.workingdir >>/yarn.lock" }}
save_yarn_cache:
parameters:
workingdir:
type: string
default: .
steps:
- save_cache:
key: yarn-v1-{{ checksum "<< parameters.workingdir >>/yarn.lock" }}
paths:
- ~/.cache/yarn # `yarn cache dir`
restore_workingdir_cache:
parameters:
platform:
type: string
default: ios
sdk_version:
type: string
default: sdk32
steps:
- restore_cache:
key: "workingdir-v1-<< parameters.platform >>-<< parameters.sdk_version >>"
save_workingdir_cache:
parameters:
workingdir:
type: string
default: /Users/distiller/project/workingdir
platform:
type: string
default: ios
sdk_version:
type: string
default: sdk32
steps:
- save_cache:
key: "workingdir-v1-<< parameters.platform >>-<< parameters.sdk_version >>"
paths:
- "<< parameters.workingdir >>/<< parameters.platform >>/<< parameters.sdk_version >>"
jobs:
build:
docker:
- image: circleci/node:8.12
executor: nix
steps:
- checkout
- setup
- restore_yarn_cache
- run: yarn
- save_yarn_cache
- run: yarn lint
- run: yarn build
test:
docker:
- image: circleci/node:8.12
unit_tests:
executor: nix
steps:
- checkout
- setup
- restore_yarn_cache
- run: yarn
- run: ./scripts/testUnitCI.sh
- save_yarn_cache
- run:
name: Run unit tests
command: ./scripts/tests/runUnitTestsCI.sh
ios_smoke_tests:
executor: mac
steps:
- install_nix
- setup
- decrypt_secrets_if_possible
- restore_yarn_cache
- run: yarn
- save_yarn_cache
- restore_workingdir_cache:
sdk_version: sdk32
platform: ios
- run:
name: Run iOS smoke tests
command: ./scripts/tests/runIOSSmokeTestsCI.sh
- save_workingdir_cache:
workingdir: /Users/distiller/project/workingdir
sdk_version: sdk32
platform: ios
android_smoke_tests:
executor: android
steps:
- setup
- setup_nix
- restore_yarn_cache
- run: yarn
- save_yarn_cache
- restore_workingdir_cache:
sdk_version: sdk32
platform: android
- run:
name: Fetch Android shell app
command: SDK_VERSION=sdk32 ./scripts/android/fetchRemoteAndroidTarball.sh
- run:
name: Run Android smoke tests
command: |
nix-shell nix/android-shell.nix --pure \
--run "./scripts/tests/runAndroidSmokeTestsCI.sh"
- save_workingdir_cache:
workingdir: ~/turtle/workingdir
sdk_version: sdk32
platform: android
workflows:
version: 2
build_and_test:
checks:
jobs:
- build
- test
tests:
jobs:
- unit_tests
- ios_smoke_tests
- android_smoke_tests
+7 -1
View File
@@ -15,4 +15,10 @@ export NODE_ENV=development
export EXPO_TURTLE_TOOLS_DIR="$(pwd)/tools"
PATH_add $EXPO_TURTLE_TOOLS_DIR/bin
if [ -z "$EXPO_UNIVERSE_DIR" ]; then
PATH_add $EXPO_TURTLE_TOOLS_DIR/bin
fi
if [[ $CI = "true" ]]; then
PATH_add $EXPO_TURTLE_TOOLS_DIR/ci/bin
fi
+3 -1
View File
@@ -1,10 +1,12 @@
!/src/node_modules
node_modules
yarn-error.log
/build/
/workingdir/*
/artifacts/*
shell.apk
node_modules
.secrets*
*.swp
+1
View File
@@ -0,0 +1 @@
v8.15.1
-1
View File
@@ -2,7 +2,6 @@ LOGGER_LEVEL="debug"
API_PROTOCOL="http"
API_HOSTNAME="localhost"
API_PORT="3000"
LOGGLY_TOKEN=""
SENTRY_DSN=""
ENVIRONMENT="development"
REDIS_URL="redis://localhost:6379/0"
+59
View File
@@ -6,6 +6,65 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
- Updated iOS shell app for SDK 32 to fix AppAuth issue
## [0.6.0] - 2019-04-09
### Changed
- Removed Branch module from Android shell apps for all SDK versions
## [0.5.15] - 2019-04-03
### Added
- Turtle smoke tests are now being executed on CircleCI from the context of this repository.
### Changed
- Updated iOS shell app for SDK 32 (fixes [this loading issue for slow connections](https://github.com/expo/expo/issues/3574)).
- Minor infra changes (doesn't affect users).
- Error logs format - error objects are now passed in an object being passed as the first argument to logger function.
### Removed
- Sending logs to loggly.
## [0.5.14] - 2019-03-22
### Added
- `--config` parameter for turtle-cli commands, which allows to specify a custom path to app.json file
### Changed
- Updated iOS shell app for SDK 32 (fixes [this notifications issue](https://github.com/expo/expo/issues/3663)).
## [0.5.13] - 2019-03-12
### Added
- Android SDK version support validation.
### Changed
- iOS Enterprise builds don't compile bitcode now.
## [0.5.12] - 2019-02-26
### Added
- Google Cloud logger (as a bunyan stream).
### Changed
- Android builder passes the manifest from a build job message if it's available.
## [0.5.11] - 2019-02-22
### Added
- Builder for iOS client apps.
## [0.5.10] - 2019-02-19
### Added
- Register Turtle version in Redis.
### Changed
- Update Android shell app for SDK 32 to fix [update](https://github.com/expo/expo/issues/3504) and [back button](https://github.com/expo/expo/issues/1786) issues.
## [0.5.9] - 2019-02-06
- Update iOS shell app for SDK 32 (fixes bare notifications [issue](https://github.com/expo/expo/issues/3223)).
## [0.5.7] - 2019-01-31
- Update Android shell app for SDK 32 to fix bundled assets issue.
## [0.5.6] - 2019-01-25
- Update iOS shell app for SDK 30, 31 and 32 (built with xcode 10.1).
## [0.5.5] - 2019-01-24
### Added
- Sending build duration in AWS SQS messages (of `FINISHED` and `ERRORED` types).
## [0.5.4] - 2019-01-22
### Changed
- Update iOS shell app for SDK32 to [`expo/expo#0847c10`](https://github.com/expo/expo/commit/0847c1073d38c366866f3d9279ddd662da47dc9b), i.e. v2.10.3 with fixed WebView navigation scheme and upgraded Facebook SDK.
## [0.5.3] - 2019-01-21
+2
View File
@@ -29,3 +29,5 @@ To submit a pull request:
## 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!
-7
View File
@@ -102,13 +102,6 @@ ENV PATH ${GRADLE_HOME}/bin:$PATH
# Install Gulp
RUN npm install -g gulp-cli
# Install Git
RUN echo 'deb http://http.debian.net/debian wheezy-backports main' >> /etc/apt/sources.list && \
apt-get update && \
apt-get -t wheezy-backports install git-core && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
ADD . /app
RUN for SDK_VERSION in `ls /app/workingdir/android/`; do \
+1 -1
View File
@@ -9,6 +9,6 @@
"^.+\\.ts$": "ts-jest"
},
"testRegex": "(test|spec)\\.ts$",
"moduleFileExtensions": ["ts", "js"],
"moduleFileExtensions": ["ts", "js", "json"],
"verbose": true
}
+1 -1
View File
@@ -9,6 +9,6 @@
"^.+\\.ts$": "ts-jest"
},
"testMatch": [ "**/__smoke__/*.test.ts" ],
"moduleFileExtensions": ["ts", "js"],
"moduleFileExtensions": ["ts", "js", "json"],
"verbose": true
}
+1 -1
View File
@@ -9,6 +9,6 @@
"^.+\\.ts$": "ts-jest"
},
"testMatch": [ "**/__tests__/*.ts" ],
"moduleFileExtensions": ["ts", "js"],
"moduleFileExtensions": ["ts", "js", "json"],
"verbose": true
}
@@ -50,8 +50,6 @@ spec:
value: exp-shell-app-assets
- name: AWS_S3_REGION
value: us-west-1
- name: LOGGLY_SUBDOMAIN
value: exponent
- name: TURTLE_TEMPORARY_FILES_DIR
value: /tmp/turtle
# vars from k8s secrets
@@ -100,11 +98,6 @@ spec:
secretKeyRef:
name: turtle-js-config
key: aws_cloudwatch_region
- name: LOGGLY_TOKEN
valueFrom:
secretKeyRef:
name: turtle-js-config
key: loggly_token
- name: REDIS_URL
valueFrom:
secretKeyRef:
+5
View File
@@ -0,0 +1,5 @@
self: super:
{
nodejs = super.nodejs-8_x;
}
+50
View File
@@ -0,0 +1,50 @@
with (import ./default.nix {});
let
ndk = androidenv.androidndk_17c.override { fullNDK = true; };
ndk_root = "${ndk}/libexec/${ndk.name}";
sdk_path = if stdenv.isDarwin
then "/Library/Android/sdk"
else "/Android/Sdk"; # intentional Capital S
in
mkShell rec {
LANG="en_US.UTF-8";
JAVA_HOME=openjdk8;
ANDROID_SDK_ROOT=builtins.getEnv("HOME") + sdk_path;
ANDROID_NDK_ROOT="${ANDROID_SDK_ROOT}/ndk-bundle";
ANDROID_HOME=ANDROID_SDK_ROOT;
ANDROID_NDK=ANDROID_NDK_ROOT;
nativeBuildInputs = [
awscli
curl
git
nodejs-8_x
openjdk8
yarn
];
passthru = { inherit ndk; };
shellHook = ''
${./install-ndk-17c.sh} ${ndk} ${ndk_root}
yes | ${androidenv.androidsdk_9_0}/bin/sdkmanager --licenses --sdk_root="$ANDROID_SDK_ROOT"
yes | ${androidenv.androidsdk_9_0}/bin/sdkmanager --sdk_root="$ANDROID_SDK_ROOT" "platforms;android-28" | grep -v =
yes | ${androidenv.androidsdk_9_0}/bin/sdkmanager --sdk_root="$ANDROID_SDK_ROOT" "build-tools;28.0.3" | grep -v =
${lib.optionalString stdenv.isLinux ''
for dep in lib lib64; do
if [ -L /$dep ] || [ ! -e /$dep ]; then
echo "Creating /$dep"
ln -s ${stdenv.cc.libc}/$dep /$dep
else
echo "Using existing /$dep"
fi
done
''}
'';
}
+19
View File
@@ -0,0 +1,19 @@
let
pin = builtins.fromJSON (builtins.readFile ./nixpkgs.json);
nixpkgs = builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs-channels/archive/${pin.rev}.tar.gz";
inherit (pin) sha256;
};
in
{ ... } @ args:
import nixpkgs (args // {
config = {
allowUnfree = true;
android_sdk.accept_license = true;
};
overlays = [ (import ./all-packages.nix) ];
})
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
scriptdir="$(dirname ${BASH_SOURCE[0]})"
shellnix="$scriptdir/android-shell.nix"
ndk=${1:-$(nix-build "$shellnix" -A ndk --no-out-link)}
ndkRoot=${2:-$(nix eval --file "$shellnix" --raw ANDROID_NDK_ROOT)}
mkdir -p "$ANDROID_NDK_ROOT" # Create full path if necessary
# Replace any existing link or directory with link
rm -r "$ANDROID_NDK_ROOT"
ln -sn "$ndkRoot" "$ANDROID_NDK_ROOT"
+7
View File
@@ -0,0 +1,7 @@
{
"url": "https://github.com/NixOS/nixpkgs-channels.git",
"rev": "80738ed9dc0ce48d7796baed5364eef8072c794d",
"date": "2018-11-17T11:18:10+01:00",
"sha256": "0anmvr6b47gbbyl9v2fn86mfkcwgpbd5lf0yf3drgm8pbv57c1dc",
"fetchSubmodules": false
}
+25 -6
View File
@@ -1,6 +1,6 @@
{
"name": "turtle-cli",
"version": "0.5.4",
"version": "0.6.0",
"description": "The command-line tool for building standalone Expo apps",
"preferGlobal": true,
"scripts": {
@@ -10,6 +10,7 @@
"watch": "EXPENV_OVERRIDES=local-common,local,local-queue expenv gulp watch:source",
"bin:turtle": "./bin/turtle.js",
"lint": "tslint -c tslint.json 'src/**/*.ts'",
"lint:fix": "yarn lint --fix",
"prepare": "yarn build",
"build": "gulp build:server",
"build:production": "./scripts/build.sh",
@@ -26,10 +27,10 @@
"test:integration": "yarn test --config jest/integration-config.json",
"test:unit": "yarn test --config jest/unit-config.json",
"test:smoke": "yarn test --config jest/smoke-config.json --forceExit",
"test:smoke:android": "yarn test:smoke --testNamePattern=android",
"test:smoke:android": "PLATFORM=android yarn test:smoke --testNamePattern=android",
"test:smoke:ios": "PLATFORM=ios yarn test:smoke --testNamePattern=iOS",
"release": "release-it",
"release:beta": "yarn release -c .release-it.beta.json --preRelease=beta",
"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",
@@ -38,6 +39,17 @@
"set-priorities:staging": "expenv babel-node scripts/setConfigurations.js",
"set-priorities:production": "EXPENV_OVERRIDES=prod expenv babel-node scripts/setConfigurations.js"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.ts": [
"yarn lint:fix",
"git add"
]
},
"bin": {
"turtle": "./bin/turtle.js"
},
@@ -88,7 +100,9 @@
"gulp-plumber": "^1.2.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-typescript": "^4.0.2",
"husky": "^1.3.1",
"jest": "^23.6.0",
"lint-staged": "^8.1.5",
"nock": "^10.0.1",
"nodemon": "^1.18.7",
"release-it": "^10.0.0",
@@ -96,14 +110,16 @@
"source-map-loader": "^0.2.3",
"ts-jest": "^23.10.4",
"tslint": "^5.10.0",
"tslint-eslint-rules": "^5.4.0",
"typescript": "^3.0.1"
},
"dependencies": {
"@expo/spawn-async": "^1.4.0",
"@google-cloud/logging-bunyan": "^0.9.5",
"async-retry": "^1.2.1",
"aws-sdk": "^2.226.1",
"bunyan": "^1.8.12",
"bunyan-debug-stream": "^1.0.8",
"bunyan-loggly": "^1.3.4",
"bunyan-sentry-stream": "^1.2.1",
"commander": "^2.15.1",
"copy": "^0.3.1",
@@ -122,6 +138,9 @@
"tar": "^4.4.6",
"uuid": "^3.2.1",
"which": "^1.3.1",
"xdl": "52.0.14-alpha.6+371be041"
"xdl": "53.1.6-alpha.2"
},
"optionalDependencies": {
"@expo/traveling-fastlane-darwin": "^1.8.1"
}
}
+1 -21
View File
@@ -1,24 +1,4 @@
#!/usr/bin/env bash
set -xeo pipefail
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../.. && pwd )"
ARTIFACTS_DIR="$ROOT_DIR/artifacts"
WORKING_DIR="$ROOT_DIR/workingdir/android"
ANDROID_SHELL_TARBALLS_DIRECTORY="$ROOT_DIR/shellTarballs/android"
mkdir -p $ARTIFACTS_DIR
for SDK_VERSION in `ls $ANDROID_SHELL_TARBALLS_DIRECTORY`; do
mkdir -p $WORKING_DIR/$SDK_VERSION
TARBALL_URI=$(head -n 1 $ANDROID_SHELL_TARBALLS_DIRECTORY/$SDK_VERSION)
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
$ROOT_DIR/scripts/fetchRemoteTarballs.sh android
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -xeo pipefail
PLATFORM=$1
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
ARTIFACTS_DIR="$ROOT_DIR/artifacts"
WORKING_DIR="$ROOT_DIR/workingdir/$PLATFORM"
SHELL_TARBALLS_DIRECTORY="$ROOT_DIR/shellTarballs/$PLATFORM"
mkdir -p $ARTIFACTS_DIR
convert_s3_to_https() {
BUCKET=`echo $1 | sed 's/s3:\/\/\([^/]*\).*/\1/'`
KEY=`echo $1 | sed 's/s3:\/\/[^/]*\/\(.*\)/\1/'`
echo "https://s3.amazonaws.com/$BUCKET/$KEY"
}
download_shell_app() {
SDK_VERSION=$1
mkdir -p $WORKING_DIR/$SDK_VERSION
TARBALL_S3_URL=$(head -n 1 $SHELL_TARBALLS_DIRECTORY/$SDK_VERSION)
TARBALL_HTTPS_URL=$(convert_s3_to_https $TARBALL_S3_URL)
curl -s $TARBALL_HTTPS_URL -o $ARTIFACTS_DIR/$SDK_VERSION.tar.gz
tar zxf $ARTIFACTS_DIR/$SDK_VERSION.tar.gz -C $WORKING_DIR/$SDK_VERSION
rm -rf $ARTIFACTS_DIR/$SDK_VERSION.tar.gz
}
if [[ -z "$SDK_VERSION" ]]; then
for SDK_VERSION in `ls $SHELL_TARBALLS_DIRECTORY`; do
download_shell_app $SDK_VERSION
done
else
if [ ! -d "$WORKING_DIR/$SDK_VERSION" ]; then
download_shell_app $SDK_VERSION
fi
fi
+1 -21
View File
@@ -1,24 +1,4 @@
#!/usr/bin/env bash
set -xeo pipefail
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../.. && pwd )"
ARTIFACTS_DIR="$ROOT_DIR/artifacts"
WORKING_DIR="$ROOT_DIR/workingdir/ios"
IOS_SHELL_TARBALLS_DIRECTORY="$ROOT_DIR/shellTarballs/ios"
mkdir -p $ARTIFACTS_DIR
for SDK_VERSION in `ls $IOS_SHELL_TARBALLS_DIRECTORY`; do
mkdir -p $WORKING_DIR/$SDK_VERSION
TARBALL_URI=$(head -n 1 $IOS_SHELL_TARBALLS_DIRECTORY/$SDK_VERSION)
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
$ROOT_DIR/scripts/fetchRemoteTarballs.sh ios
+11
View File
@@ -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 "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
+7
View File
@@ -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
+2 -2
View File
@@ -48,10 +48,10 @@ setup_remote() {
echo "Shell apps already fetched"
else
echo "Fetching shell apps"
if [ "$PLATFORM" != 'ios']; then
if [ "$PLATFORM" != 'ios' ]; then
$ROOT_DIR/scripts/android/fetchRemoteAndroidTarball.sh
fi
if [ "$PLATFORM" != 'android']; then
if [ "$PLATFORM" != 'android' ]; then
$ROOT_DIR/scripts/ios/fetchRemoteIosTarball.sh
fi
fi
@@ -1,37 +1,25 @@
#!/usr/bin/env bash
set -ueo pipefail
export NODE_ENV=test
export ENVIRONMENT="development"
export AWS_ACCESS_KEY_ID="aws_id"
export AWS_SECRET_ACCESS_KEY="secret"
export AWS_SQS_IOS_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_ios_in.fifo"
export AWS_SQS_IOS_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_ios_in.fifo"
export AWS_SQS_ANDROID_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_android_in.fifo"
export AWS_SQS_ANDROID_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_android_in.fifo"
export AWS_SQS_OUT_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_staging_out.fifo"
export AWS_SQS_ANDROID_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_android_in.fifo"
export AWS_SQS_ANDROID_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_android_in_priority.fifo"
export AWS_SQS_IOS_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_ios_in.fifo"
export AWS_SQS_IOS_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_ios_in_priority.fifo"
export AWS_SQS_OUT_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_out.fifo"
export AWS_SQS_REGION="aws.west2"
export AWS_S3_BUCKET="exp-shell-app-assets"
export AWS_S3_REGION="us-west-1"
export AWS_CLOUDWATCH_REGION="us-west-1"
export API_PROTOCOL="http"
export API_HOSTNAME="localhost"
export API_PORT="3000"
export API_HOSTNAME="staging.expo.io"
export API_PORT="443"
export REDIS_URL="noop"
export REDIS_CONFIG_URL="noop"
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
$ROOT_DIR/scripts/android/fetchRemoteAndroidTarball.sh
yarn test:smoke:android
smoke_result=$?
yarn test:unit
unit_result=$?
[[ $smoke_result != 0 || $unit_result != 0 ]] && \
echo && \
echo "Tests failed." && \
exit -1
exit 0
@@ -1,13 +1,15 @@
#!/usr/bin/env bash
set -ueo pipefail
export NODE_ENV=test
export ENVIRONMENT="development"
export AWS_SQS_IOS_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_ios_in.fifo"
export AWS_SQS_ANDROID_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_android_in.fifo"
export AWS_SQS_IOS_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_ios_in_priority.fifo"
export AWS_SQS_ANDROID_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_android_in_priority.fifo"
export AWS_SQS_OUT_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_jakub_out.fifo"
export AWS_SQS_ANDROID_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_android_in.fifo"
export AWS_SQS_ANDROID_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_android_in_priority.fifo"
export AWS_SQS_IOS_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_ios_in.fifo"
export AWS_SQS_IOS_PRIORITY_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_ios_in_priority.fifo"
export AWS_SQS_OUT_QUEUE_URL="https://sqs.us-west-2.amazonaws.com/274251141632/turtle_jobs_local_dominik_out.fifo"
export AWS_SQS_REGION="aws.west2"
export AWS_S3_BUCKET="exp-shell-app-assets"
export AWS_S3_REGION="us-west-1"
@@ -18,18 +20,7 @@ export API_PORT="443"
export REDIS_URL="noop"
export REDIS_CONFIG_URL="noop"
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
$ROOT_DIR/scripts/ios/fetchRemoteIosTarball.sh
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../.. && pwd )"
SDK_VERSION=sdk32 $ROOT_DIR/scripts/ios/fetchRemoteIosTarball.sh
yarn test:smoke:ios
smoke_result=$?
yarn test:unit
unit_result=$?
[[ $smoke_result != 0 || $unit_result != 0 ]] && \
echo && \
echo "Tests failed." && \
exit -1
exit 0
@@ -1,5 +1,7 @@
#!/usr/bin/env bash
set -ueo pipefail
export NODE_ENV=test
export ENVIRONMENT="development"
@@ -21,6 +23,3 @@ export REDIS_URL="noop"
export REDIS_CONFIG_URL="noop"
yarn test:unit
unit_result=$?
exit $unit_result
+1 -1
View File
@@ -1 +1 @@
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
+1 -1
View File
@@ -1 +1 @@
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
+1 -1
View File
@@ -1 +1 @@
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
+1 -1
View File
@@ -1 +1 @@
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
+1 -1
View File
@@ -1 +1 @@
s3://exp-artifacts/android-shell-builder-67bd02667e29221707f8c1dd11e739eada25ef55.tar.gz
s3://exp-artifacts/android-shell-builder-c19ce0d88a153f481fd790e23bdcd6f2d5ea7fd6.tar.gz
+1 -1
View File
@@ -1 +1 @@
s3://exp-artifacts/android-shell-builder-5c339ff7bab6f2161d378cc70e1bf04d32ad98dd.tar.gz
s3://exp-artifacts/android-shell-builder-89b2d24c86737d5a489c6a18467e31b2f9ce9005.tar.gz
+1 -1
View File
@@ -1 +1 @@
s3://exp-artifacts/android-shell-builder-cb03fdc12dc74f699a6d3f22219cbcd30e01ac46.tar.gz
s3://exp-artifacts/android-shell-builder-0cab68670fc50ce1dc16b7d86ca7976870062c39.tar.gz
+1
View File
@@ -0,0 +1 @@
s3://exp-artifacts/ios-expo-client-15bb883c22173d5b01df19421580170e776041d0.tar.gz
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -1 +1 @@
s3://exp-artifacts/ios-shell-builder-sdk-latest-f3de2d71b10c058501e99e0b1ce9b5a991e291be.tar.gz
s3://exp-artifacts/ios-shell-builder-sdk-latest-dc981c7110b7d508de8e9c352064dc3d745c9f72.tar.gz
File diff suppressed because it is too large. Load diff
Binary file not shown.
+1 -1
View File
@@ -179,7 +179,7 @@ function _pushMetrics(metrics: IMetricsChunk[]) {
metrics.forEach((metricsChunk) => {
cloudWatch.putMetricData(metricsChunk, (err) => {
if (err) {
logger.warn('[cloudwatch]', err);
logger.warn({ err }, '[cloudwatch]');
}
});
});
+1 -1
View File
@@ -7,7 +7,7 @@ export function sum(metrics: IMetric[]) {
const reduced = metrics.reduce((acc: IMetric | null, i: IMetric) => {
let result = acc;
if (!result) {
result = {...i};
result = { ...i };
} else {
result.Value += i.Value;
}
+4 -4
View File
@@ -36,7 +36,7 @@ export async function receiveMessage(priority: string) {
}
})
.catch((err) => {
logger.error('Receive error', err);
logger.error({ err }, 'Receive error');
throw err;
});
});
@@ -62,13 +62,13 @@ export async function changeMessageVisibility(priority: string, receiptHandle: s
}
// Every VISIBILITY_TIMEOUT_SEC / 3 seconds we are telling AWS SQS
// that we're still proccessing the message, so it does not
// that we're still processing the message, so it does not
// send the build job to another turtle agent
export function changeMessageVisibilityRecurring(priority: string, receiptHandle: string, jobId: string) {
return setInterval(() => {
if (getCurrentJobId() === jobId) {
changeMessageVisibility(priority, receiptHandle).catch((err) => {
logger.warn('Error at change msg visibility', err);
logger.warn({ err }, 'Error at change msg visibility');
});
}
}, (VISIBILITY_TIMEOUT_SEC * 1000) / 3);
@@ -86,6 +86,6 @@ export async function sendMessage(jobId: string, status: JOB_UPDATE_TYPE, data =
return await sqs.sendMessage(params).promise();
});
} catch (err) {
logger.error(`Error sending SQS message: ${JSON.stringify(params)}}`, err);
logger.error({ err }, `Error sending SQS message: ${JSON.stringify(params)}}`);
}
}
+2 -1
View File
@@ -20,7 +20,8 @@ function setCommonCommandOptions(cmd: any) {
.option(
'--release-channel <channel-name>',
'pull from specified release channel (default: default)',
);
)
.option('-c --config <config-file>', 'specify a path to app.json');
}
export default {
+1 -2
View File
@@ -33,8 +33,7 @@ async function setupAction(program: any, cmd: any, platform: string, os?: string
await setup(platform, sdkVersion);
l.info('it\'s all set!');
} catch (err) {
logger.error(`Failed to setup environment for ${platform} builds`);
logger.error(err.stack);
logger.error({ err }, `Failed to setup environment for ${platform} builds`);
if (err instanceof ErrorWithCommandHelp) {
cmd.help();
} else if (err instanceof ErrorWithProgramHelp) {
+4 -4
View File
@@ -17,16 +17,16 @@ Command.prototype.asyncAction = function asyncAction(asyncFn: (...asyncFnArgs: a
return this.action(async (...args) => {
try {
await checkForUpdateAsync();
} catch (e) {
logger.warn('Failed to check for turtle-cli update.');
} catch (err) {
logger.warn({ err }, 'Failed to check for turtle-cli update.');
}
return await asyncFn(...args);
});
};
export function run(programName: string) {
runAsync(programName).catch((e) => {
logger.error('Uncaught Error', e);
runAsync(programName).catch((err) => {
logger.error({ err }, 'Uncaught Error');
process.exit(1);
});
}
+3 -2
View File
@@ -6,13 +6,14 @@ import { ExponentTools } from 'xdl';
import download from 'turtle/bin/setup/utils/downloader';
import { ensureToolsAreInstalled, IToolDefinition } from 'turtle/bin/setup/utils/toolsDetector';
import { IShellAppDirectoryConfig } from 'turtle/builders/utils/workingdir';
import config from 'turtle/config';
import logger from 'turtle/logger';
const l = logger.withFields({ buildPhase: 'setting up environment' });
interface IShellAppFormaters {
formatShellAppDirectory: (sdkVersion: string) => string;
formatShellAppDirectory: (config: IShellAppDirectoryConfig) => string;
formatShellAppTarballUriPath: (sdkMajorVersion: string) => string;
}
@@ -27,7 +28,7 @@ export async function ensureShellAppIsPresent(
formatters: IShellAppFormaters,
postDownloadAction?: PostDownloadAction,
) {
const workingdir = formatters.formatShellAppDirectory(sdkVersion);
const workingdir = formatters.formatShellAppDirectory({ sdkVersion });
if (await fs.pathExists(workingdir)) {
return;
}
+2 -2
View File
@@ -26,9 +26,9 @@ export async function ensureToolsAreInstalled(tools: IToolDefinition[]) {
} catch (err) {
isAnyToolMissing = true;
if (!testFn) {
l.error(`${command} is missing in your $PATH`);
l.error({ err }, `${command} is missing in your $PATH`);
}
l.error(missingDescription);
l.error({ err }, missingDescription);
}
}
if (isAnyToolMissing) {
+2 -3
View File
@@ -60,7 +60,7 @@ export function createBuilderAction({
publicUrl: cmd.publicUrl,
};
const appJSON = await ProjectUtils.loadAppJSON(projectDirArg);
const appJSON = await ProjectUtils.loadAppJSON(projectDirArg, cmd.config);
const sdkVersion = _.get(appJSON, 'expo.sdkVersion');
await setup(platform, sdkVersion);
const credentials = await prepareCredentials(cmd);
@@ -72,8 +72,7 @@ export function createBuilderAction({
const job = await sanitizeJob(rawJob);
await builder(job);
} catch (err) {
logger.error(`Failed to build standalone app: ${err.message}`);
logger.error(err.stack);
logger.error({ err }, 'Failed to build standalone app');
if (err instanceof ErrorWithCommandHelp) {
command.help();
} else if (err instanceof ErrorWithProgramHelp) {
+9 -4
View File
@@ -1,12 +1,17 @@
import fs from 'fs-extra';
import path from 'path';
export async function loadAppJSON(projectDirArg: string) {
const projectDir = resolveAbsoluteDir(projectDirArg);
const appJSONPath = path.join(projectDir, 'app.json');
export async function loadAppJSON(projectDirArg: string, config: string | null) {
let appJSONPath;
if (config) {
appJSONPath = resolveAbsoluteDir(config);
} else {
const projectDir = resolveAbsoluteDir(projectDirArg);
appJSONPath = path.join(projectDir, 'app.json');
}
const appJSONExists = await fs.pathExists(appJSONPath);
if (!appJSONExists) {
throw new Error(`Couldn't find app.json in ${projectDir} directory.`);
throw new Error(`Couldn't find app.json.`);
} else {
return require(appJSONPath);
}
+17
View File
@@ -0,0 +1,17 @@
class BuildError extends Error {
public reason: BuildErrorReason;
constructor(reason: BuildErrorReason, message: string) {
super(message);
this.name = 'BuildError';
this.reason = reason;
}
}
enum BuildErrorReason {
SESSION_EXPIRED = 'session-expired',
PROVISIONING_PROFILE_MISSING = 'provisioning-profile-missing',
}
export default BuildError;
export { BuildErrorReason };
+8 -5
View File
@@ -10,11 +10,13 @@ import { formatShellAppDirectory } from 'turtle/builders/utils/android/workingdi
import * as commonUtils from 'turtle/builders/utils/common';
import * as imageHelpers from 'turtle/builders/utils/image';
import { uploadBuildToS3 } from 'turtle/builders/utils/uploader';
import { ensureCanBuildSdkVersion } from 'turtle/builders/utils/version';
import config from 'turtle/config';
import { IAndroidCredentials, IJob, IJobResult } from 'turtle/job';
import logger from 'turtle/logger';
export default async function buildAndroid(jobData: IJob): Promise<IJobResult> {
await ensureCanBuildSdkVersion(jobData);
const credentials = await getOrCreateCredentials(jobData);
const apkFilePath = await runShellAppBuilder(jobData, credentials);
const randomHex = uuidv4().replace(/-/g, '');
@@ -60,20 +62,21 @@ async function runShellAppBuilder(
const outputFilePath = path.join(temporaryFilesRoot, `shell-signed-${jobData.id}.apk`);
const { manifest: { sdkVersion: sdkVersionFromManifest = null } = {}, sdkVersion: sdkVersionFromJob } = jobData;
const sdkVersion = sdkVersionFromJob || sdkVersionFromManifest;
const workingDir = formatShellAppDirectory(sdkVersion);
const { config: jobConfig, manifest, sdkVersion: sdkVersionFromJob } = jobData;
const sdkVersion = _.get(manifest, 'sdkVersion', sdkVersionFromJob);
const workingDir = formatShellAppDirectory({ sdkVersion });
try {
await AndroidShellApp.createAndroidShellAppAsync({
url: commonUtils.getExperienceUrl(jobData),
sdkVersion: _.get(jobData, 'manifest.sdkVersion') || jobData.sdkVersion,
sdkVersion,
keystore: tempKeystorePath,
manifest,
alias: credentials.keystoreAlias,
keystorePassword: credentials.keystorePassword,
keyPassword: credentials.keyPassword,
privateConfigFile: tempShellAppConfigPath,
releaseChannel: jobData.config.releaseChannel,
releaseChannel: jobConfig.releaseChannel,
workingDir,
outputFile: outputFilePath,
});
+2 -3
View File
@@ -33,7 +33,7 @@ async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, m
l.info('building and signing IPA');
const {
credentials: { provisioningProfile, certPassword, teamId, password },
credentials: { provisioningProfile, certPassword, teamId },
config: { bundleIdentifier: bundleIdentifierFromConfig },
} = job;
@@ -51,9 +51,8 @@ async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, m
certPath: ctx.tempCertPath,
certPassword,
teamID: teamId,
password,
bundleIdentifier,
workingDir: ctx.workingDir,
workspacePath: ctx.workspacePath,
manifest,
});
await ipaBuilder.build();
+53 -12
View File
@@ -22,9 +22,11 @@ export interface IContext {
tempCertPath: string;
uploadPath: string;
workingDir: string;
workspacePath: string;
applicationFilesSrc: string;
}
const { DEFAULT_EXPOKIT_WORKSPACE_NAME } = IosShellApp;
const { EXPOKIT_APP, EXPONENT_APP } = IosShellApp;
const { BUILD_TYPES } = IOS;
export function createBuilderContext(job: IJob): IContext {
@@ -39,7 +41,7 @@ export function createBuilderContext(job: IJob): IContext {
} = job;
const sdkVersion = sdkVersionFromJob || sdkVersionFromManifest;
const workingDir = formatShellAppDirectory(sdkVersion);
const workingDir = formatShellAppDirectory({ sdkVersion, buildType: buildType! });
const context: any = {
appDir: join(config.directories.temporaryFilesRoot, appUUID),
@@ -55,24 +57,63 @@ export function createBuilderContext(job: IJob): IContext {
context.tempCertPath = join(context.appDir, 'cert.p12');
context.baseArchiveDir = join(context.appDir, 'archive');
if (buildType === BUILD_TYPES.SIMULATOR) {
context.outputPath = join(context.appDir, 'archive.tar.gz');
context.uploadPath = join(context.appDir, 'archive.tar.gz');
context.archiveDir = join(
context.baseArchiveDir,
'Release',
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.app`,
if (buildType === BUILD_TYPES.CLIENT) {
context.applicationFilesSrc = join(
workingDir,
'expo-client-build',
'**',
'*',
);
} else if (buildType === BUILD_TYPES.ARCHIVE) {
} else {
context.applicationFilesSrc = join(
workingDir,
'shellAppBase-builds',
buildType as string,
'**',
'*',
);
}
if (buildType === BUILD_TYPES.ARCHIVE) {
context.outputPath = join(context.appDir, 'archive.xcarchive');
context.uploadPath = join(context.buildDir, 'archive.ipa');
context.archiveDir = join(
context.baseArchiveDir,
'Release',
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.xcarchive`,
`${EXPOKIT_APP}.xcarchive`,
'Products',
'Applications',
`${DEFAULT_EXPOKIT_WORKSPACE_NAME}.app`,
`${EXPOKIT_APP}.app`,
);
context.workspacePath = path.join(
workingDir,
'shellAppWorkspaces',
'ios',
'default',
`${EXPOKIT_APP}.xcworkspace`,
);
} else if (buildType === BUILD_TYPES.CLIENT) {
context.outputPath = join(context.appDir, 'archive.xcarchive');
context.uploadPath = join(context.buildDir, 'archive.ipa');
context.archiveDir = join(
context.baseArchiveDir,
`${EXPONENT_APP}.xcarchive`,
'Products',
'Applications',
`${EXPONENT_APP}.app`,
);
context.workspacePath = path.join(
workingDir,
'ios',
`${EXPONENT_APP}.xcworkspace`,
);
} else if (buildType === BUILD_TYPES.SIMULATOR) {
context.outputPath = join(context.appDir, 'archive.tar.gz');
context.uploadPath = join(context.appDir, 'archive.tar.gz');
context.archiveDir = join(
context.baseArchiveDir,
'Release',
`${EXPOKIT_APP}.app`,
);
}
+14 -32
View File
@@ -1,24 +1,23 @@
import path from 'path';
import fs from 'fs-extra';
import pick from 'lodash/pick';
import { ExponentTools } from 'xdl';
import buildArchive from 'turtle/builders/ios/archive';
import { createBuilderContext, IContext } from 'turtle/builders/ios/context';
import buildSimulator from 'turtle/builders/ios/simulator';
import { logErrorOnce } from 'turtle/builders/utils/common';
import prepareAdHocBuildCredentials from 'turtle/builders/utils/ios/adhocBuild';
import { uploadBuildToS3 } from 'turtle/builders/utils/uploader';
import { ensureCanBuildSdkVersion } from 'turtle/builders/utils/version';
import config from 'turtle/config';
import { IOS, PLATFORMS } from 'turtle/constants/index';
import { IOS } from 'turtle/constants/index';
import { IJob, IJobResult } from 'turtle/job';
const { BUILD_TYPES } = IOS;
let SUPPORTED_SDK_VERSIONS: number[];
export default async function iosBuilder(job: IJob): Promise<IJobResult> {
await ensureCanBuildSdkVersion(job);
if (job.config.buildType !== BUILD_TYPES.CLIENT) {
await ensureCanBuildSdkVersion(job);
}
const ctx = createBuilderContext(job);
@@ -26,10 +25,15 @@ export default async function iosBuilder(job: IJob): Promise<IJobResult> {
await initBuilder(ctx);
const { buildType } = job.config;
if (buildType === BUILD_TYPES.SIMULATOR) {
await buildSimulator(ctx, job);
} else if (buildType === BUILD_TYPES.ARCHIVE) {
if (buildType === BUILD_TYPES.CLIENT) {
await prepareAdHocBuildCredentials(job);
}
if ([BUILD_TYPES.ARCHIVE, BUILD_TYPES.CLIENT].includes(buildType!)) {
await buildArchive(ctx, job);
} else if (buildType === BUILD_TYPES.SIMULATOR) {
await buildSimulator(ctx, job);
} else {
throw new Error(`Unsupported iOS build type: ${buildType}`);
}
@@ -60,25 +64,3 @@ async function cleanup(ctx: IContext) {
const getTemporaryDirs = (ctx: IContext) =>
Object.values(pick(ctx, ['appDir', 'provisioningProfileDir']));
async function ensureCanBuildSdkVersion(job: IJob) {
if (config.builder.useLocalWorkingDir) {
return;
}
if (!SUPPORTED_SDK_VERSIONS) {
SUPPORTED_SDK_VERSIONS = await findSupportedSdkVersions();
}
const targetMajorSdkVersion = ExponentTools.parseSdkMajorVersion(job.sdkVersion || job.manifest.sdkVersion);
if (!SUPPORTED_SDK_VERSIONS.includes(targetMajorSdkVersion)) {
throw new Error(`Unsupported SDK Version!`);
}
}
async function findSupportedSdkVersions(): Promise<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), 10));
}
+9 -7
View File
@@ -2,7 +2,7 @@ import path from 'path';
import fs from 'fs-extra';
import uuidv4 from 'uuid/v4';
import { AndroidKeystore } from 'xdl';
import { Credentials } from 'xdl';
import * as sqs from 'turtle/aws/sqs';
import { UPDATE_CREDENTIALS } from 'turtle/constants/build';
@@ -21,13 +21,15 @@ async function getOrCreateCredentials(jobData: IJob): Promise<IAndroidCredential
credentials.keystoreAlias = Buffer.from(jobData.experienceName).toString('base64');
const androidPackage = jobData.manifest.android.package;
await AndroidKeystore.createKeystore({
keystorePassword: credentials.keystorePassword,
keyPassword: credentials.keyPassword,
keystoreFilename,
keystoreAlias: credentials.keystoreAlias,
await Credentials.Android.createKeystore(
{
keystorePath: keystoreFilename,
keystorePassword: credentials.keystorePassword,
keyPassword: credentials.keyPassword,
keyAlias: credentials.keystoreAlias,
},
androidPackage,
});
);
credentials.keystore = (await fs.readFile(keystoreFilename)).toString('base64');
l.info('Keystore created successfully');
+2 -1
View File
@@ -2,10 +2,11 @@ import path from 'path';
import { ExponentTools } from 'xdl';
import { IShellAppDirectoryConfig } from 'turtle/builders/utils/workingdir';
import config from 'turtle/config';
import { PLATFORMS } from 'turtle/constants';
export function formatShellAppDirectory(sdkVersion: string) {
export function formatShellAppDirectory({ sdkVersion }: IShellAppDirectoryConfig) {
const majorSdkVersion = ExponentTools.parseSdkMajorVersion(sdkVersion);
return config.builder.useLocalWorkingDir
? path.join(config.directories.workingDir, 'local')
+4 -4
View File
@@ -18,15 +18,15 @@ export async function resizeIconWithSharpAsync(iconSizePx: number, iconFilename:
await fs.writeFile(filename, buffer);
}
export async function getImageDimensionsWithSharpAsync(basename: string, dirname: string) {
const filename = path.join(dirname, basename);
export async function getImageDimensionsWithSharpAsync(dirname: string, filename: string) {
const filepath = path.join(dirname, filename);
// PLEASE DON'T REMOVE THE FOLLOWING LINE (sharp caches files taking path as the cache key)
sharp.cache(false);
try {
const meta = await sharp(filename).metadata();
return [meta.width, meta.height];
const { width, height } = await sharp(filepath).metadata();
return { width, height };
} catch (e) {
return null;
}
+99
View File
@@ -0,0 +1,99 @@
import path from 'path';
import spawnAsync from '@expo/spawn-async';
import fs from 'fs-extra';
import isEmpty from 'lodash/isEmpty';
import { IosCodeSigning } from 'xdl';
import * as sqs from 'turtle/aws/sqs';
import BuildError, { BuildErrorReason } from 'turtle/builders/BuildError';
import { UPDATE_CREDENTIALS } from 'turtle/constants/build';
import { IJob } from 'turtle/job';
import logger from 'turtle/logger';
import { isOffline } from 'turtle/turtleContext';
// tslint:disable-next-line:no-var-requires
const travelingFastlane = process.platform === 'darwin' ? require('@expo/traveling-fastlane-darwin')() : null;
async function prepareAdHocBuildCredentials(job: IJob) {
if (process.platform !== 'darwin') {
throw new Error('This function should be called only on macOS!');
}
const { bundleIdentifier } = job.config;
const { certP12, certPassword, teamId, appleSession, udids } = job.credentials;
const certSerialNumber = IosCodeSigning.findP12CertSerialNumber(certP12, certPassword);
const args = [
teamId,
udids!.join(','),
bundleIdentifier,
certSerialNumber || '__last__',
];
try {
const credentials = await runFastlaneAction(
travelingFastlane.manageAdHocProvisioningProfile,
args,
{ env: { FASTLANE_SESSION: appleSession } },
);
if (isEmpty(credentials)) {
if (!job.credentials.provisioningProfile) {
// a user has generated the provisioning profile for his app, but he probably deleted it from Expo servers, ...
throw new BuildError(
BuildErrorReason.PROVISIONING_PROFILE_MISSING,
`Provisioning profile is missing on Expo servers.`,
);
}
return;
}
logger.info('New ad hoc provisioning profile successfully created');
job.credentials.provisioningProfile = credentials.provisioningProfile;
if (isOffline()) {
const provisioningProfilePath = path.join(job.projectDir, `adhoc.mobileprovision`);
await fs.writeFile(provisioningProfilePath, Buffer.from(credentials.provisioningProfile, 'base64'));
logger.info(`Saved provisioning profile to ${provisioningProfilePath}`);
} else {
await sqs.sendMessage(job.id, UPDATE_CREDENTIALS, {
provisioningProfileId: credentials.provisioningProfileId,
provisioningProfile: credentials.provisioningProfile,
});
logger.info('Ad Hoc provisioning profile sent to storage successfully');
}
} catch (e) {
if (e instanceof BuildError) {
logger.error('Apple Session expired! Please authenticate again using expo-cli.');
}
throw e;
}
}
async function runFastlaneAction(action: string, args: string[], { env }: any) {
const { stderr } = await spawnAsync(
action,
args,
{
stdio: ['inherit', 'pipe', 'pipe'],
env: { ...process.env, ...env },
},
);
const { result, ...rest } = JSON.parse(stderr.trim());
if (result === 'failure') {
const { reason, rawDump, type } = rest;
let errorMsg = `Reason: ${reason}`;
if (rawDump) {
errorMsg += `, raw: ${JSON.stringify(rawDump)}`;
}
if (type === 'session-expired') {
throw new BuildError(BuildErrorReason.SESSION_EXPIRED, errorMsg);
} else {
throw new Error(errorMsg);
}
} else {
return rest;
}
}
export default prepareAdHocBuildCredentials;
+3 -3
View File
@@ -16,7 +16,7 @@ export async function create(ctx: IContext): Promise<IKeychain> {
l.info('done creating keychain');
return keychainInfo;
} catch (err) {
l.error('unable to create keychain');
l.error({ err }, 'unable to create keychain');
throw err;
}
}
@@ -32,7 +32,7 @@ export async function remove(ctx: IContext, keychainPath: string) {
l.info('done deleting keychain');
return keychainInfo;
} catch (err) {
l.error('unable to delete keychain');
l.error({ err }, 'unable to delete keychain');
throw err;
}
}
@@ -57,7 +57,7 @@ export async function importCert(
await IosKeychain.importIntoKeychain({ keychainPath, certPath, certPassword });
l.info('done importing distribution certificate into keychain');
} catch (err) {
l.error('unable to import distribution certificate into keychain');
l.error({ err }, 'unable to import distribution certificate into keychain');
throw err;
}
}
+17 -15
View File
@@ -8,7 +8,7 @@ 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 { PLATFORMS } from 'turtle/constants';
import { IOS } from 'turtle/constants/index';
import { IJob } from 'turtle/job';
import logger from 'turtle/logger/index';
@@ -18,14 +18,8 @@ export default async function runShellAppBuilder(ctx: IContext, job: IJob): Prom
const { config: jobConfig, manifest, sdkVersion: sdkVersionFromJob } = job;
const { buildType, releaseChannel } = jobConfig;
const sdkVersion = _.get(manifest, 'sdkVersion', sdkVersionFromJob);
const applicationFilesSrc = path.join(
ctx.workingDir,
'shellAppBase-builds',
buildType as string,
'**',
'*',
);
await copyAsync(applicationFilesSrc, ctx.baseArchiveDir);
await copyAsync(ctx.applicationFilesSrc, ctx.baseArchiveDir);
logger.info(
{ buildPhase: 'icons setup' },
@@ -34,18 +28,26 @@ export default async function runShellAppBuilder(ctx: IContext, job: IJob): Prom
ImageUtils.setResizeImageFunction(imageHelpers.resizeIconWithSharpAsync);
ImageUtils.setGetImageDimensionsFunction(imageHelpers.getImageDimensionsWithSharpAsync);
const shellAppParams = {
url: commonUtils.getExperienceUrl(job),
sdkVersion,
action: 'configure',
type: buildType,
releaseChannel,
archivePath: ctx.archiveDir,
privateConfigData: job.config,
verbose: true,
output: ctx.outputPath,
expoSourcePath: path.join(ctx.workingDir, PLATFORMS.IOS),
expoSourcePath: path.join(ctx.workingDir, 'ios'),
manifest,
output: ctx.outputPath,
verbose: true,
};
if (buildType === IOS.BUILD_TYPES.CLIENT) {
Object.assign(shellAppParams, {
workspacePath: path.join(ctx.workingDir, 'ios'),
});
} else {
Object.assign(shellAppParams, {
url: commonUtils.getExperienceUrl(job),
releaseChannel,
sdkVersion,
});
}
logger.info({ buildPhase: 'configuring NSBundle' }, 'configuring NSBundle...');
return await IosShellApp.configureAndCopyArchiveAsync(shellAppParams);
+12 -6
View File
@@ -2,12 +2,18 @@ import path from 'path';
import { ExponentTools } from 'xdl';
import { IShellAppDirectoryConfig } from 'turtle/builders/utils/workingdir';
import config from 'turtle/config';
import { PLATFORMS } from 'turtle/constants';
import { IOS, PLATFORMS } from 'turtle/constants';
export function formatShellAppDirectory(sdkVersion: string) {
const majorVersion = ExponentTools.parseSdkMajorVersion(sdkVersion);
return config.builder.useLocalWorkingDir
? path.join(config.directories.workingDir, 'local')
: path.join(config.directories.workingDir, PLATFORMS.IOS, `sdk${majorVersion}`);
export function formatShellAppDirectory({ sdkVersion, buildType }: IShellAppDirectoryConfig) {
const { workingDir } = config.directories;
if (buildType === IOS.BUILD_TYPES.CLIENT) {
return path.join(workingDir, PLATFORMS.IOS, 'client');
} else if (config.builder.useLocalWorkingDir) {
return path.join(workingDir, 'local');
} else {
const majorVersion = ExponentTools.parseSdkMajorVersion(sdkVersion);
return path.join(workingDir, PLATFORMS.IOS, `sdk${majorVersion}`);
}
}
+30
View File
@@ -0,0 +1,30 @@
import fs from 'fs-extra';
import path from 'path';
import { ExponentTools } from 'xdl';
import config from 'turtle/config';
import { IJob } from 'turtle/job';
let SUPPORTED_SDK_VERSIONS: number[];
export async function ensureCanBuildSdkVersion(job: IJob) {
if (config.builder.useLocalWorkingDir) {
return;
}
if (!SUPPORTED_SDK_VERSIONS) {
SUPPORTED_SDK_VERSIONS = await findSupportedSdkVersions(job.platform);
}
const targetMajorSdkVersion = ExponentTools.parseSdkMajorVersion(job.sdkVersion || job.manifest.sdkVersion);
if (!SUPPORTED_SDK_VERSIONS.includes(targetMajorSdkVersion)) {
throw new Error(`Unsupported SDK Version!`);
}
}
async function findSupportedSdkVersions(platform: 'android' | 'ios'): Promise<number[]> {
const SDK_DIR_PREFIX = 'sdk';
const files = await fs.readdir(path.join(config.directories.workingDir, platform));
const sdks = files.filter((file) => file.startsWith(SDK_DIR_PREFIX));
return sdks.map((sdk) => parseInt(sdk.substr(SDK_DIR_PREFIX.length), 10));
}
+6
View File
@@ -0,0 +1,6 @@
import { IOS } from 'turtle/constants';
export interface IShellAppDirectoryConfig {
sdkVersion: string;
buildType?: IOS.BUILD_TYPES;
}
+4 -6
View File
@@ -8,7 +8,7 @@ export default {
env: env('NODE_ENV'),
hostname: env('HOSTNAME', os.hostname()),
deploymentEnv: env('ENVIRONMENT'),
platform: env('PLATFORM', PLATFORMS.IOS),
platform: env('PLATFORM', PLATFORMS.IOS) as PLATFORMS,
aws: {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_SECRET_ACCESS_KEY'),
@@ -47,11 +47,9 @@ export default {
client: {
level: env('CLIENT_LOGGER_LEVEL', 'info'),
},
loggly: {
token: env('LOGGLY_TOKEN', ''),
subdomain: env('LOGGLY_SUBDOMAIN', ''),
buffer: envNum('LOGGLY_BUFFER', 100),
},
},
google: {
credentials: env('GOOGLE_APPLICATION_CREDENTIALS', ''),
},
sentry: {
dsn: env('SENTRY_DSN', ''),
+4 -4
View File
@@ -1,9 +1,9 @@
import * as BUILD from './build';
import * as IOS from './ios';
export const PLATFORMS = {
IOS: 'ios',
ANDROID: 'android',
};
export enum PLATFORMS {
IOS = 'ios',
ANDROID = 'android',
}
export { IOS, BUILD };
+5 -4
View File
@@ -1,4 +1,5 @@
export const BUILD_TYPES = {
ARCHIVE: 'archive',
SIMULATOR: 'simulator',
};
export enum BUILD_TYPES {
ARCHIVE = 'archive',
CLIENT = 'client',
SIMULATOR = 'simulator',
}
+6 -3
View File
@@ -1,3 +1,5 @@
import { IOS } from 'turtle/constants';
export interface IJob {
platform: 'android' | 'ios';
experienceName: string;
@@ -8,7 +10,7 @@ export interface IJob {
releaseChannel: string;
publicUrl?: string;
// ios
buildType?: string;
buildType?: IOS.BUILD_TYPES;
bundleIdentifier?: string;
};
credentials: {
@@ -16,13 +18,14 @@ export interface IJob {
keystore?: string;
keystoreAlias?: string;
keystorePassword?: string;
// ios
keyPassword?: string;
// ios
certP12?: string;
certPassword?: string;
provisioningProfile?: string;
password?: string;
teamId?: string;
appleSession?: string;
udids?: string[];
};
projectDir: string;
fakeUploadDir?: string;
+28 -15
View File
@@ -1,23 +1,19 @@
import path from 'path';
import fs from 'fs-extra';
import _ from 'lodash';
import * as sqs from 'turtle/aws/sqs';
import builders from 'turtle/builders';
import BuildError from 'turtle/builders/BuildError';
import { logErrorOnce } from 'turtle/builders/utils/common';
import config from 'turtle/config';
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 { checkShouldExit, setCurrentJobId, turtleVersion } 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()) {
logger.warn('Exiting due to previously received termination signal.');
@@ -51,7 +47,7 @@ export async function getJob() {
return job;
}
} catch (err) {
logger.error('Error at receiving messages', err);
logger.error({ err }, 'Error at receiving messages');
}
}
}
@@ -75,20 +71,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(
@@ -123,7 +120,9 @@ function failAfterMaxJobTime(priority: string, receiptHandle: string, job: any)
}
async function build(job: any) {
const startTimestamp = Date.now();
const s3Url = await logger.init(job);
const calculateBuildDuration = () => Math.ceil((Date.now() - startTimestamp) / 1000);
try {
await sqs.sendMessage(job.id, BUILD.JOB_STATES.IN_PROGRESS, {
@@ -131,11 +130,25 @@ async function build(job: any) {
logFormat: 'json',
});
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();
let reason;
if (err instanceof BuildError) {
reason = err.reason;
}
sqs.sendMessage(job.id, BUILD.JOB_STATES.ERRORED, {
turtleVersion,
buildDuration,
...reason && { reason },
});
return false;
} finally {
await logger.cleanup(job);
@@ -147,6 +160,6 @@ async function deleteMessage(priority: string, receiptHandle: string) {
setCurrentJobId(null);
await sqs.deleteMessage(priority, receiptHandle);
} catch (err) {
logger.error('Error at deleting msg', err);
logger.error({ err }, 'Error at deleting msg');
}
}
+6 -4
View File
@@ -12,11 +12,13 @@ export default baseJobSchema.concat(
}),
credentials: Joi.object()
.keys({
certP12: Joi.string(),
certPassword: Joi.string(),
teamId: Joi.string(),
certP12: Joi.string().required(),
certPassword: Joi.string().required(),
teamId: Joi.string().required(),
// it's not required for adhoc Expo Client builds
provisioningProfile: Joi.string(),
password: Joi.string(),
appleSession: Joi.string(),
udids: Joi.array().items(Joi.string()),
})
.when('config.buildType', {
is: IOS.BUILD_TYPES.SIMULATOR,
-48
View File
@@ -1,48 +0,0 @@
import EventEmitter from 'events';
import BunyanLoggly from 'bunyan-loggly';
import config from 'turtle/config';
import * as constants from 'turtle/constants/logger';
import { IJob } from 'turtle/job';
export interface ILogglyConfig {
token: string;
subdomain: string;
tags: string[];
}
interface ILogglyExtraFields {
jobID: string;
experienceName: string;
}
export default class HackyLogglyStream extends EventEmitter {
private logglyStream: BunyanLoggly;
private extraFields: ILogglyExtraFields | null;
constructor(logglyConfig: ILogglyConfig) {
super();
this.logglyStream = new BunyanLoggly(logglyConfig, config.logger.loggly.buffer);
this.extraFields = null;
}
public init(job: IJob) {
this.extraFields = {
jobID: job.id,
experienceName: job.experienceName,
};
}
public cleanup() {
this.extraFields = null;
}
public write(rec: any) {
const { msg: message, hostname: host, level, ...other } = rec;
this.logglyStream.write({
message,
level: constants.LEVELS_MAPPING[level],
host,
...other,
data: this.extraFields || {},
});
}
}
+16 -23
View File
@@ -6,10 +6,14 @@ import { Client as RavenClient } from 'raven';
import config from 'turtle/config';
import * as constants from 'turtle/constants/logger';
import { IJob } from 'turtle/job';
import HackyLogglyStream from 'turtle/logger/hackyLogglyStream';
import S3Stream from 'turtle/logger/s3Stream';
import { isOffline } from 'turtle/turtleContext';
// type error when using import
// https://github.com/googleapis/nodejs-logging-bunyan/issues/241
// tslint:disable-next-line:no-var-requires
const { LoggingBunyan } = require('@google-cloud/logging-bunyan');
interface IStream {
stream: any;
type?: string;
@@ -18,7 +22,6 @@ interface IStream {
}
export const s3logger = new S3Stream();
let logglyStream: HackyLogglyStream;
const streams: IStream[] = [];
@@ -47,45 +50,35 @@ if (config.sentry.dsn && config.deploymentEnv !== 'development') {
});
}
if (config.logger.loggly.token) {
const logglyConfig = {
token: config.logger.loggly.token,
subdomain: config.logger.loggly.subdomain,
// prettier-ignore
tags: [
'app-shell-apps',
`platform.${config.platform}`,
`environment.${config.deploymentEnv}`,
],
if (config.google.credentials) {
const resource = {
type: 'generic_node',
labels: {
node_id: config.hostname,
location: '', // default value
namespace: '', // default value
},
};
logglyStream = new HackyLogglyStream(logglyConfig);
streams.push({
type: 'raw',
stream: logglyStream,
});
streams.push(new LoggingBunyan({ name: 'turtle', resource }).stream('info'));
}
const logger = bunyan.createLogger({
name: 'turtle',
level: config.logger.level,
platform: config.platform,
environment: config.deploymentEnv,
streams,
});
logger.withFields = (extraFields: any) => withFields(logger, extraFields);
logger.init = async (job: IJob) => {
if (logglyStream) {
logglyStream.init(job);
}
return await s3logger.init(job);
};
logger.cleanup = async () => {
// a little hacky, but works
logger.info({ lastBuildLog: true }, 'this is the last log from this build');
if (logglyStream) {
logglyStream.cleanup();
}
await s3logger.waitForLogger();
};
-13
View File
@@ -1,13 +0,0 @@
export = BunyanLoggly;
declare class BunyanLoggly {
constructor(options: BunyanLoggly.IOptions, bufferLength?: number, bufferTimeout?: number, logglyCallback?: any);
public write(data: any): void;
}
declare namespace BunyanLoggly {
interface IOptions {
token: string;
subdomain: string;
}
}
+16 -4
View File
@@ -4,9 +4,12 @@ import config from 'turtle/config';
import { doJob } from 'turtle/jobManager';
import logger from 'turtle/logger';
import setup from 'turtle/setup';
import { checkShouldExit, setShouldExit } from 'turtle/turtleContext';
import { checkShouldExit, setShouldExit, turtleVersion } from 'turtle/turtleContext';
import { getRedisClient, RedisClient } from 'turtle/utils/redis';
process.on('unhandledRejection', (err) => logger.error('Unhandled promise rejection:', err));
const REDIS_TURTLE_VERSION_KEY = 'turtle:version';
process.on('unhandledRejection', (err) => logger.error({ err }, 'Unhandled promise rejection'));
function handleExit() {
if (checkShouldExit()) {
@@ -30,16 +33,25 @@ async function main() {
if (setup[config.platform]) {
await setup[config.platform]();
}
try {
const redis = await getRedisClient(RedisClient.Configuration);
await redis.set(REDIS_TURTLE_VERSION_KEY, turtleVersion);
logger.info(`Register Turtle version ${turtleVersion} in Redis`);
} catch (err) {
logger.error({ err }, 'Failed to register Turtle version in Redis');
}
while (true) {
try {
await doJob();
} catch (err) {
logger.error('Failed to do a job', err);
logger.error({ err }, 'Failed to do a job');
}
}
}
main()
.then(() => logger.error('This should never happen...'))
.catch((err) => logger.error('Something went terribly wrong :(', err))
.catch((err) => logger.error({ err }, 'Something went terribly wrong'))
.then(() => process.exit(1));
+5
View File
@@ -1,5 +1,10 @@
import fs from 'fs-extra';
import path from 'path';
import config from 'turtle/config';
const { version: turtleVersion } = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
export { turtleVersion };
let shouldExit = false;
let currentJobId: string | null = null;
+8 -5
View File
@@ -41,6 +41,10 @@ export function createDefaultConfigurationKey(platform = config.platform) {
}
export async function getPriorities() {
if (config.env === 'test') {
return NORMAL_CONFIGURATION;
}
try {
const redis = await getRedisClient(RedisClient.Configuration);
if (redis.getConfig === undefined) {
@@ -56,24 +60,23 @@ export async function getPriorities() {
logger.debug(`Using configuration at index ${configurationsIndex} pulled from redis`);
const { error, value: configValue } = configSchema.validate(configurations[configurationsIndex]);
if (error) {
logger.warn(error);
logger.warn({ err: error });
throw new Error('Received configuration is not valid');
}
return configValue;
} catch (err) {
logger.warn(err);
logger.warn({ err });
const defaultConfig = JSON.parse(await redis.get(createDefaultConfigurationKey()));
const { error, value: defaultConfigValue } = configSchema.validate(defaultConfig);
if (error) {
logger.warn(error);
logger.warn({ err: error });
throw new Error('Received default configuration is not valid');
}
logger.warn('Using default configuration pulled from redis');
return defaultConfigValue;
}
} catch (err) {
logger.warn(err);
logger.warn('Using configuration chosen locally');
logger.warn({ err }, 'Using configuration chosen locally');
if (Math.random() < 0.7) {
return HIGH_CONFIGURATION;
} else {
+2 -2
View File
@@ -52,7 +52,7 @@ export async function getRedisClient(type = RedisClient.Default) {
try {
redisClients[type] = await connect(MILLIS_CONNECTION_TIMEOUT, type);
} catch (err) {
logger.error(err);
logger.error({ err });
}
}
return redisClients[type];
@@ -66,7 +66,7 @@ export async function checkIfCancelled(jobId: string) {
if (config.deploymentEnv === 'development') {
logger.warn('Did you turn on redis server? Run `yarn start-docker` in server/www');
}
logger.error(err);
logger.error({ err });
return false;
}
}
Executable
+3
View File
@@ -0,0 +1,3 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../nix" awscli --command aws "$@"
+3
View File
@@ -0,0 +1,3 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../nix" awscli --command aws_bash_completer "$@"
+3
View File
@@ -0,0 +1,3 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../nix" awscli --command aws_completer "$@"
+3
View File
@@ -0,0 +1,3 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../nix" git-crypt --command git-crypt "$@"
+19
View File
@@ -0,0 +1,19 @@
#! /usr/bin/env bash
set -euo pipefail
scriptDir=$(dirname "$0")
nixDir="$scriptDir/../../nix"
nix build -f $nixDir --no-link "$@"
for package in "$@"; do
for b in $(ls $(nix path-info -f $nixDir $package)/bin); do
cat << EOF > "$scriptDir/$b"
#! /usr/bin/env bash
# This file was generated by $(basename "$0")
exec nix run -f "\$(dirname "\$0")/../../nix" $package --command $b "\$@"
EOF
chmod +x "$scriptDir/$b"
done
done
+3
View File
@@ -0,0 +1,3 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../../nix" nodejs-8_x --command node "$@"
+3
View File
@@ -0,0 +1,3 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../../nix" nodejs-8_x --command npm "$@"
+3
View File
@@ -0,0 +1,3 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../../nix" nodejs-8_x --command npx "$@"
+3
View File
@@ -0,0 +1,3 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../../nix" yarn --command yarn "$@"
+3
View File
@@ -0,0 +1,3 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../../nix" yarn --command yarnpkg "$@"
+4 -1
View File
@@ -5,7 +5,10 @@
"rules": {
"quotemark": [true, "single", "jsx-double"],
"object-literal-sort-keys": false,
"object-curly-spacing": true,
"no-namespace": false
},
"rulesDirectory": []
"rulesDirectory": [
"node_modules/tslint-eslint-rules/dist/rules"
]
}
+4159 -592
View File
File diff suppressed because it is too large. Load diff