Compare commits

...
Author SHA1 Message Date
Dominik Sokal 4ebf6c1d2c use release-it to handle releases 2019-01-18 11:38:20 +01:00
Wojciech Kozyra 4b29da8f5b fixes for expo outside universe (#8)
* fixes for expo outside universe

* move test scripts + fix typos
2019-01-18 11:36:08 +01:00
Dominik Sokal 74d6b37b00 remove jsconfig.json 2019-01-17 16:09:39 +01:00
Dominik Sokal d8049d8b83 Get rid of eslint and fix tslint errors (#9)
* get rid of eslint

* fix tslint errors

* run lint before building turtle
2019-01-17 16:00:33 +01:00
77 changed files with 1549 additions and 993 deletions

No files matched your search

+4 -8
View File
@@ -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
-21
View File
@@ -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": {}
}
}
}
+3
View File
@@ -6,3 +6,6 @@
shell.apk
node_modules
.secrets*
*.swp
*.swo
+9
View File
@@ -0,0 +1,9 @@
{
"git": {
"commit": false,
"tag": true,
"push": true,
"requireUpstream": false,
"tagName": "v${version}"
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"npm": {
"tag": "latest"
},
"git": {
"requireUpstream": false,
"tagName": "v${version}"
},
"github": {
"release": true
}
}
-11
View File
@@ -1,11 +0,0 @@
{
"compilerOptions": {
"target": "es2017",
"allowSyntheticDefaultImports": false,
"baseUrl": "./",
"paths": {
"turtle/*": ["src/*"],
}
},
"exclude": ["node_modules", "dist", "build"]
}
+14 -15
View File
@@ -4,20 +4,19 @@
"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": "release-it",
"release:beta": "yarn release -c .release-it.beta.json --preRelease=beta",
"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,12 +91,12 @@
"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",
"tslint": "^5.10.0",
"typescript": "^3.0.1",
"typescript-eslint-parser": "^21.0.2"
"typescript": "^3.0.1"
},
"dependencies": {
"async-retry": "^1.2.1",
+7 -1
View File
@@ -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
+2 -2
View File
@@ -7,11 +7,11 @@ if [ -z "$1" ]; then
exit 1
fi
PLATFORM=$1
export PLATFORM=$1
yarn
yarn run build
yarn init-workingdir:remote-$PLATFORM
yarn set-workingdir:remote
if [[ -z "${TARBALL}" ]]; then
echo "TARBALL is not set, building tarball skipped"
-22
View File
@@ -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
+7 -5
View File
@@ -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
+7 -1
View File
@@ -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
+86
View File
@@ -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,8 @@ export API_PORT="3000"
export REDIS_URL="noop"
export REDIS_CONFIG_URL="noop"
PLATFORM=android yarn set-workingdir:remote
yarn test:smoke:android
smoke_result=$?
+1 -1
View File
@@ -18,7 +18,7 @@ export API_PORT="443"
export REDIS_URL="noop"
export REDIS_CONFIG_URL="noop"
yarn init-workingdir:remote-ios
PLATFORM=ios yarn set-workingdir:remote
yarn test:smoke:ios
smoke_result=$?
File renamed without changes.
+4 -5
View File
@@ -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
View File
@@ -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);
}
+5 -5
View File
@@ -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];
}
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
});
}
+2
View File
@@ -0,0 +1,2 @@
export class ErrorWithCommandHelp extends Error {
}
+2
View File
@@ -0,0 +1,2 @@
export class ErrorWithProgramHelp extends Error {
}
+9 -8
View File
@@ -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;
+4 -4
View File
@@ -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),
}
};
+10 -9
View File
@@ -1,11 +1,11 @@
import fs from 'fs-extra';
import _ from 'lodash';
import uuid from 'uuid';
import fs from 'fs-extra';
import builder from 'turtle/builders/ios';
import { ErrorWithCommandHelp } from 'turtle/bin/commands/errors';
import { IOS, PLATFORMS } from 'turtle/constants';
import { ErrorWithCommandHelp } from 'turtle/bin/commands/ErrorWithCommandHelp';
import { createBuilderAction } from 'turtle/bin/utils/builder';
import builder from 'turtle/builders/ios';
import { IOS, PLATFORMS } from 'turtle/constants';
export default (program: any, setCommonCommandOptions: any) => {
const command = program.command('build:ios [project-dir]');
@@ -16,16 +16,16 @@ export default (program: any, setCommonCommandOptions: any) => {
'-t --type <build>',
'type of build: archive|simulator',
/^(archive|simulator)$/i,
'archive'
'archive',
)
.option('--team-id <apple-teamId>', 'Apple Team ID')
.option(
'--dist-p12-path <dist.p12>',
'path to your Distribution Certificate P12 (please provide password as EXPO_IOS_DIST_P12_PASSWORD env variable)'
'path to your Distribution Certificate P12 (please provide password as EXPO_IOS_DIST_P12_PASSWORD env variable)',
)
.option('--provisioning-profile-path <.mobileprovision>', 'path to your Provisioning Profile')
.description(
'Build a standalone IPA for your project, signed and ready for submission to the Apple App Store.'
'Build a standalone IPA for your project, signed and ready for submission to the Apple App Store.',
)
.asyncAction(
createBuilderAction({
@@ -36,7 +36,7 @@ export default (program: any, setCommonCommandOptions: any) => {
builder,
platform: PLATFORMS.IOS,
os: 'darwin',
})
}),
);
};
@@ -68,7 +68,8 @@ const prepareCredentials = async (cmd: any) => {
if (!credentialsExist) {
throw new ErrorWithCommandHelp(
'Please provide all required credentials - Apple Team ID, Distribution Certificate P12 (with password) and Provisioning Profile'
'Please provide all required credentials'
+ '- Apple Team ID, Distribution Certificate P12 (with password) and Provisioning Profile',
);
}
-2
View File
@@ -1,2 +0,0 @@
export class ErrorWithProgramHelp extends Error {}
export class ErrorWithCommandHelp extends Error {}
+5 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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`,
);
}
}
+12 -12
View File
@@ -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 -4
View File
@@ -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];
}
+3 -3
View File
@@ -5,10 +5,10 @@ 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';
@@ -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');
+4 -4
View File
@@ -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 -1
View File
@@ -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) {
+11 -9
View File
@@ -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);
+2 -3
View File
@@ -1,9 +1,9 @@
import { URL } from 'url';
import fs from 'fs';
import { URL } from 'url';
import ProgressBar from 'progress';
import request from 'request';
import requestProgress from 'request-progress';
import ProgressBar from 'progress';
const S3_PROTOCOL = 's3:';
const PROGRESS_BAR_CONFIG = {
@@ -38,7 +38,6 @@ export default async function download(url: string, destPath: string) {
});
}
function convertS3ToHttpsUrl(s3UrlRaw: string) {
const { host: s3Bucket, pathname: s3Path, protocol } = new URL(s3UrlRaw);
if (protocol !== S3_PROTOCOL) {
+1 -1
View File
@@ -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 {
+11 -7
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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;
+7 -4
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
import buildIOS from './ios';
import buildAndroid from './android';
import buildIOS from './ios';
export default {
ios: buildIOS,
+6 -6
View File
@@ -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);
+15 -7
View File
@@ -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);
}
+4 -4
View File
@@ -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);
@@ -76,9 +76,9 @@ async function ensureCanBuildSdkVersion(job: IJob) {
}
}
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));
}
+2 -2
View File
@@ -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' });
+2 -2
View File
@@ -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) {
+3 -1
View File
@@ -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 -1
View File
@@ -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 -2
View File
@@ -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 {
+6 -6
View File
@@ -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',
+2
View File
@@ -1,5 +1,7 @@
import path from 'path';
import { ExponentTools } from 'xdl';
import config from 'turtle/config';
import { PLATFORMS } from 'turtle/constants';
+10 -4
View File
@@ -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'),
),
},
};
+1 -1
View File
@@ -5,7 +5,7 @@ export enum JOB_STATES {
FINISHED = 'finished',
ERRORED = 'errored',
SENT_TO_QUEUE = 'sent-to-queue',
};
}
export const UPDATE_CREDENTIALS = 'update-credentials';
-1
View File
@@ -7,4 +7,3 @@ export const PLATFORMS = {
};
export { IOS, BUILD };
+11 -9
View File
@@ -1,19 +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 } = require('../package.json');
const { version: turtleVersion } = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
function _maybeExit() {
if (checkShouldExit()) {
@@ -31,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;
+1 -1
View File
@@ -12,5 +12,5 @@ export default baseJobSchema.concat(
keystoreAlias: Joi.string(),
keystore: Joi.string(),
}),
})
}),
);
+1 -1
View File
@@ -23,5 +23,5 @@ export default baseJobSchema.concat(
then: Joi.optional(),
otherwise: Joi.required(),
}),
})
}),
);
+11 -11
View File
@@ -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
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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';
+3 -3
View File
@@ -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';
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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))));
}
}
+3 -3
View 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';
+4 -3
View File
@@ -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
View File
@@ -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();
+5
View File
@@ -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
View File
@@ -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": []
}
+1066 -623
View File
File diff suppressed because it is too large. Load diff