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
54 changed files with 227 additions and 220 deletions

No files matched your search

+6 -5
View File
@@ -3,7 +3,7 @@ version: 2.1
executors:
nix: &nix
docker:
- image: lnl7/nix:2.1.2
- 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
@@ -137,11 +137,12 @@ commands:
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
@@ -153,7 +154,7 @@ jobs:
- run: yarn
- save_yarn_cache
- run:
name: Run iOS smoke tests
name: Run unit tests
command: ./scripts/tests/runUnitTestsCI.sh
ios_smoke_tests:
+4
View File
@@ -18,3 +18,7 @@ export EXPO_TURTLE_TOOLS_DIR="$(pwd)/tools"
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
-1
View File
@@ -1 +0,0 @@
fixtures/
+1
View File
@@ -0,0 +1 @@
v8.15.1
+1 -1
View File
@@ -115,7 +115,7 @@
},
"dependencies": {
"@expo/spawn-async": "^1.4.0",
"@google-cloud/logging-bunyan": "^0.10.1",
"@google-cloud/logging-bunyan": "^0.9.5",
"async-retry": "^1.2.1",
"aws-sdk": "^2.226.1",
"bunyan": "^1.8.12",
+1 -1
View File
@@ -34,7 +34,7 @@ async function performSmokeTest(recording: string) {
});
}
await jobManager.doJob(logger);
await jobManager.doJob();
if (!fixtureExists) {
await fs.writeFile(recording, JSON.stringify(nock.recorder.play()));
}
+18 -19
View File
@@ -11,6 +11,7 @@ import {
MetricRegistrationObject,
} from 'turtle/aws/cloudwatch/types';
import config from 'turtle/config';
import logger from 'turtle/logger';
interface IState {
metrics: IMetric[];
@@ -37,12 +38,12 @@ const cloudWatch = new aws.CloudWatch({
secretAccessKey: config.aws.secretAccessKey,
});
export function init(logger: any) {
export function init() {
if (config.cloudwatch.disabled) {
return;
}
state.intervalId = setInterval(_dumpMetrics(logger), config.cloudwatch.intervalMs);
state.intervalId = setInterval(_dumpMetrics, config.cloudwatch.intervalMs);
}
export function addMetricData({
@@ -110,25 +111,23 @@ function _calculateGroupHash(metricName: string, extraDimensions: any[]) {
return `${metricName}${dimString}`;
}
function _dumpMetrics(logger: any) {
return () => {
if (!state.metrics.length && Object.keys(state.registeredMetrics).length < 0) {
// empty metrics list, skipping
return;
}
function _dumpMetrics() {
if (!state.metrics.length && Object.keys(state.registeredMetrics).length < 0) {
// empty metrics list, skipping
return;
}
logger.trace(
'[cloudwatch] lengths for state.metrics=%d, state.registeredMetrics=%d',
state.metrics.length,
Object.keys(state.registeredMetrics).length,
);
logger.trace(
'[cloudwatch] lenghts for state.metrics=%d, state.registeredMetrics=%d',
state.metrics.length,
Object.keys(state.registeredMetrics).length,
);
_addEmptyMetrics(state.metrics);
const formattedMetrics = _formatMetrics(state.metrics);
_pushMetrics(formattedMetrics, logger);
_addEmptyMetrics(state.metrics);
const formattedMetrics = _formatMetrics(state.metrics);
_pushMetrics(formattedMetrics);
state.metrics = [];
};
state.metrics = [];
}
function _addEmptyMetrics(metrics: IMetric[]) {
@@ -175,7 +174,7 @@ function _reduceMetrics(metrics: IMetric[]) {
);
}
function _pushMetrics(metrics: IMetricsChunk[], logger: any) {
function _pushMetrics(metrics: IMetricsChunk[]) {
logger.trace('[cloudwatch] pushing metrics', JSON.stringify(metrics, null, '\t'));
metrics.forEach((metricsChunk) => {
cloudWatch.putMetricData(metricsChunk, (err) => {
+4 -3
View File
@@ -5,6 +5,7 @@ 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';
const sqs = new AWS.SQS({
@@ -15,7 +16,7 @@ const sqs = new AWS.SQS({
const VISIBILITY_TIMEOUT_SEC = 30;
export async function receiveMessage(priority: string, logger: any) {
export async function receiveMessage(priority: string) {
const params = {
MaxNumberOfMessages: 1,
QueueUrl: QUEUE_URL(priority),
@@ -63,7 +64,7 @@ export async function changeMessageVisibility(priority: string, receiptHandle: s
// Every VISIBILITY_TIMEOUT_SEC / 3 seconds we are telling AWS SQS
// 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, logger: any) {
export function changeMessageVisibilityRecurring(priority: string, receiptHandle: string, jobId: string) {
return setInterval(() => {
if (getCurrentJobId() === jobId) {
changeMessageVisibility(priority, receiptHandle).catch((err) => {
@@ -73,7 +74,7 @@ export function changeMessageVisibilityRecurring(priority: string, receiptHandle
}, (VISIBILITY_TIMEOUT_SEC * 1000) / 3);
}
export async function sendMessage(jobId: string, status: JOB_UPDATE_TYPE, data = {}, logger: any) {
export async function sendMessage(jobId: string, status: JOB_UPDATE_TYPE, data = {}) {
const params = {
MessageBody: JSON.stringify({ jobId, status, ...data }),
QueueUrl: OUTPUT_QUEUE_URL(),
+2 -2
View File
@@ -2,8 +2,8 @@ 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 (logger: any) => (program: any) => {
return builder(program, setCommonCommandOptions, logger);
return (program: any) => {
return builder(program, setCommonCommandOptions);
};
}
+5 -6
View File
@@ -1,9 +1,8 @@
import builders from 'turtle/bin/commands/build';
import setup from 'turtle/bin/commands/setup';
export default {
setupIOS: setup.ios,
setupAndroid: setup.android,
buildIOS: builders.ios,
buildAndroid: builders.android,
};
export const setupIOS = setup.ios;
export const setupAndroid = setup.android;
export const buildIOS = builders.ios;
export const buildAndroid = builders.android;
+5 -4
View File
@@ -4,6 +4,9 @@ import { ErrorWithCommandHelp } from 'turtle/bin/commands/ErrorWithCommandHelp';
import { ErrorWithProgramHelp } from 'turtle/bin/commands/ErrorWithProgramHelp';
import setup from 'turtle/bin/setup/setup';
import { PLATFORMS } from 'turtle/constants';
import logger from 'turtle/logger';
const l = logger.withFields({ buildPhase: 'setting up environment' });
function createSetupCommand(platform: string, os?: string) {
return (program: any) => {
@@ -20,16 +23,14 @@ function createSetupCommand(platform: string, os?: string) {
};
}
async function setupAction(program: any, cmd: any, platform: string, logger: any, os?: string) {
async function setupAction(program: any, cmd: any, platform: string, os?: string) {
try {
if (os && process.platform !== os) {
throw new Error('We don\'t support running standalone app builds for this platform on your operating system');
}
const { sdkVersion } = cmd;
const l = logger.child({ buildPhase: 'setting up environment' });
await setup(platform, l, sdkVersion);
await setup(platform, sdkVersion);
l.info('it\'s all set!');
} catch (err) {
logger.error({ err }, `Failed to setup environment for ${platform} builds`);
+2 -2
View File
@@ -4,7 +4,7 @@ import program, { Command } from 'commander';
import fs from 'fs-extra';
import { LoggerDetach, ModuleVersion } from 'xdl';
import commands from 'turtle/bin/commands';
import * as commands from 'turtle/bin/commands';
import logger from 'turtle/logger';
const { name, version } = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'));
@@ -33,7 +33,7 @@ export function run(programName: string) {
async function runAsync(programName: string) {
program.version(version);
Object.values(commands).map((command) => command(logger)).forEach((command) => registerCommand(program, command));
Object.values(commands).forEach((command) => registerCommand(program, command));
program.parse(process.argv);
const subCommand = process.argv[2];
+13 -13
View File
@@ -12,6 +12,7 @@ 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: IToolDefinition[] = [
@@ -39,24 +40,25 @@ const REQUIRED_TOOLS: IToolDefinition[] = [
},
},
];
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
const l = logger.withFields(LOGGER_FIELDS);
export default async function setup(logger: any, sdkVersion?: string) {
await checkSystem(REQUIRED_TOOLS, logger);
await prepareAndroidEnv(logger);
export default async function setup(sdkVersion?: string) {
await checkSystem(REQUIRED_TOOLS);
await prepareAndroidEnv();
if (sdkVersion) {
await ensureShellAppIsPresent(
sdkVersion,
{ formatShellAppDirectory, formatShellAppTarballUriPath },
logger,
_shellAppPostDownloadAction,
);
}
}
async function prepareAndroidEnv(logger: any) {
async function prepareAndroidEnv() {
await fs.ensureDir(config.directories.androidDependenciesDir);
const sdkConfig = await ensureAndroidSDKIsPresent(logger);
const ndkConfig = await ensureAndroidNDKIsPresent(logger);
const sdkConfig = await ensureAndroidSDKIsPresent();
const ndkConfig = await ensureAndroidNDKIsPresent();
_setEnvVars({ ...sdkConfig.envVars, ...ndkConfig.envVars });
_alterPath([...sdkConfig.path, ...sdkConfig.path]);
}
@@ -65,23 +67,21 @@ function formatShellAppTarballUriPath(sdkMajorVersion: string) {
return path.join(config.directories.shellTarballsDir, PLATFORMS.ANDROID, `sdk${sdkMajorVersion}`);
}
async function _shellAppPostDownloadAction(workingdir: string, logger: any) {
async function _shellAppPostDownloadAction(workingdir: string) {
const inWorkingdir = (filename: string) => path.join(workingdir, filename);
await fs.move(inWorkingdir('package.json'), inWorkingdir('exponent-package.json'));
await fs.move(inWorkingdir('universe-package.json'), inWorkingdir('package.json'));
await _installNodeModules(workingdir, logger);
await _installNodeModules(workingdir);
await fs.move(inWorkingdir('package.json'), inWorkingdir('universe-package.json'));
await fs.move(inWorkingdir('exponent-package.json'), inWorkingdir('package.json'));
// TODO: remove following lines after upgrading android shell app
const toolsPublicDir = path.join(workingdir, 'tools-public');
await _installNodeModules(toolsPublicDir, logger);
await _installNodeModules(toolsPublicDir);
}
async function _installNodeModules(cwd: string, logger: any) {
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
const l = logger.child(LOGGER_FIELDS);
async function _installNodeModules(cwd: string) {
l.info(`installing dependencies in ${cwd} directory...`);
const command = await _shouldUseYarnOrNpm();
await ExponentTools.spawnAsyncThrowError(command, ['install'], {
+5 -2
View File
@@ -7,17 +7,20 @@ 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';
const ANDROID_NDK_URL = `https://dl.google.com/android/repository/android-ndk-r10e-${os.platform()}-x86_64.zip`;
export default async function ensureAndroidNDKIsPresent(logger: any) {
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
const l = logger.withFields(LOGGER_FIELDS);
export default async function ensureAndroidNDKIsPresent() {
const androidNdkDir = path.join(config.directories.androidDependenciesDir, 'ndk');
if (!(await fs.pathExists(androidNdkDir))) {
await fs.ensureDir(androidNdkDir);
const androidNdkDownloadPath = formatArtifactDownloadPath(ANDROID_NDK_URL);
try {
const l = logger.child({ buildPhase: 'setting up environment' });
l.info('Downloading Android NDK');
await fs.ensureDir(config.directories.artifactsDir);
await download(ANDROID_NDK_URL, androidNdkDownloadPath);
+5 -2
View File
@@ -8,17 +8,20 @@ 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';
const ANDROID_SDK_URL = 'https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip';
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
export default async function ensureAndroidSDKIsPresent(logger: any) {
const l = logger.withFields(LOGGER_FIELDS);
export default async function ensureAndroidSDKIsPresent() {
const androidSdkDir = path.join(config.directories.androidDependenciesDir, 'sdk');
if (!(await fs.pathExists(androidSdkDir))) {
await fs.ensureDir(androidSdkDir);
const androidSdkDownloadPath = formatArtifactDownloadPath(ANDROID_SDK_URL);
try {
const l = logger.child(LOGGER_FIELDS);
l.info('Downloading Android SDK');
await fs.ensureDir(config.directories.artifactsDir);
await download(ANDROID_SDK_URL, androidSdkDownloadPath);
+3 -3
View File
@@ -34,13 +34,13 @@ const REQUIRED_TOOLS: IToolDefinition[] = [
},
];
export default async function setup(logger: any, sdkVersion?: string) {
await checkSystem(REQUIRED_TOOLS, logger);
export default async function setup(sdkVersion?: string) {
await checkSystem(REQUIRED_TOOLS);
if (sdkVersion) {
await ensureShellAppIsPresent(sdkVersion, {
formatShellAppDirectory,
formatShellAppTarballUriPath,
}, logger);
});
}
}
+3 -3
View File
@@ -4,15 +4,15 @@ 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, logger: any, sdkVersion?: string) {
export default async function setup(platform: string, sdkVersion?: string) {
if (sdkVersion && !isSemver(sdkVersion)) {
throw new Error('SDK version is not valid.');
}
if (platform === PLATFORMS.IOS) {
return await setupIos(logger, sdkVersion);
return await setupIos(sdkVersion);
} else if (platform === PLATFORMS.ANDROID) {
return await setupAndroid(logger, sdkVersion);
return await setupAndroid(sdkVersion);
} else {
throw new Error('This should never happen :(');
}
+12 -16
View File
@@ -8,54 +8,50 @@ 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: (config: IShellAppDirectoryConfig) => string;
formatShellAppTarballUriPath: (sdkMajorVersion: string) => string;
}
type PostDownloadAction = (workingdir: string, logger: any) => void;
type PostDownloadAction = (workingdir: string) => void;
export async function checkSystem(requiredTools: IToolDefinition[], logger: any) {
await ensureToolsAreInstalled(requiredTools, logger);
export async function checkSystem(requiredTools: IToolDefinition[]) {
await ensureToolsAreInstalled(requiredTools);
}
export async function ensureShellAppIsPresent(
sdkVersion: string,
formatters: IShellAppFormaters,
logger: any,
postDownloadAction?: PostDownloadAction,
) {
const workingdir = formatters.formatShellAppDirectory({ sdkVersion });
const l = logger.child({ buildPhase: 'setting up environment' });
if (await fs.pathExists(workingdir)) {
return;
}
l.info(`shell app for SDK ${sdkVersion} doesn't exist, downloading...`);
await _downloadShellApp(sdkVersion, workingdir, formatters, l);
await _downloadShellApp(sdkVersion, workingdir, formatters);
if (postDownloadAction) {
await postDownloadAction(workingdir, logger);
await postDownloadAction(workingdir);
}
}
async function _downloadShellApp(
sdkVersion: string,
targetDirectory: string,
formatters: IShellAppFormaters,
logger: any,
) {
async function _downloadShellApp(sdkVersion: string, targetDirectory: string, formatters: IShellAppFormaters) {
const shellAppTarballS3Uri = await _readShellAppTarballS3Uri(sdkVersion, formatters);
const tarballDownloadTargetPath = formatArtifactDownloadPath(shellAppTarballS3Uri);
await fs.ensureDir(config.directories.artifactsDir);
await download(shellAppTarballS3Uri, tarballDownloadTargetPath);
logger.info('shell app has been downloaded');
l.info('shell app has been downloaded');
await fs.ensureDir(targetDirectory);
logger.info('extracting shell app (this may take a while)...');
l.info('extracting shell app (this may take a while)...');
await tar.x({
file: tarballDownloadTargetPath,
C: targetDirectory,
});
logger.info('shell app extracted');
l.info('shell app extracted');
}
export function formatArtifactDownloadPath(uri: string) {
+4 -2
View File
@@ -1,7 +1,10 @@
import util from 'util';
import _which from 'which';
import logger from 'turtle/logger';
const which = util.promisify(_which);
const l = logger.withFields({ buildPhase: 'setting up environment' });
export interface IToolDefinition {
command: string;
@@ -9,7 +12,7 @@ export interface IToolDefinition {
testFn?: () => Promise<boolean>;
}
export async function ensureToolsAreInstalled(tools: IToolDefinition[], logger: any) {
export async function ensureToolsAreInstalled(tools: IToolDefinition[]) {
let isAnyToolMissing = false;
for (const { command, missingDescription, testFn } of tools) {
try {
@@ -21,7 +24,6 @@ export async function ensureToolsAreInstalled(tools: IToolDefinition[], logger:
await which(command);
}
} catch (err) {
const l = logger.child({ buildPhase: 'setting up environment' });
isAnyToolMissing = true;
if (!testFn) {
l.error({ err }, `${command} is missing in your $PATH`);
+2 -2
View File
@@ -5,10 +5,10 @@ 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';
export function createBuilderAction({
logger,
program,
command,
prepareCredentials,
@@ -62,7 +62,7 @@ export function createBuilderAction({
const appJSON = await ProjectUtils.loadAppJSON(projectDirArg, cmd.config);
const sdkVersion = _.get(appJSON, 'expo.sdkVersion');
await setup(platform, logger, sdkVersion);
await setup(platform, sdkVersion);
const credentials = await prepareCredentials(cmd);
const rawJob = {
...buildJobObject(appJSON, args, credentials),
+5 -6
View File
@@ -13,17 +13,17 @@ 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, logger: any): Promise<IJobResult> {
export default async function buildAndroid(jobData: IJob): Promise<IJobResult> {
await ensureCanBuildSdkVersion(jobData);
const credentials = await getOrCreateCredentials(jobData, logger);
const apkFilePath = await runShellAppBuilder(jobData, credentials, logger);
const credentials = await getOrCreateCredentials(jobData);
const apkFilePath = await runShellAppBuilder(jobData, credentials);
const randomHex = uuidv4().replace(/-/g, '');
const s3Filename = `${jobData.experienceName}-${randomHex}-signed.apk`;
const s3FileKey = `android/${s3Filename}`;
const fakeUploadFilename = s3Filename.replace('/', '\\');
const artifactUrl = await uploadBuildToS3({
logger,
uploadPath: apkFilePath,
s3FileKey,
...config.builder.fakeUpload && {
@@ -40,7 +40,6 @@ export default async function buildAndroid(jobData: IJob, logger: any): Promise<
async function runShellAppBuilder(
jobData: IJob,
credentials: IAndroidCredentials,
logger: any,
): Promise<string> {
const { temporaryFilesRoot } = config.directories;
await fs.ensureDir(temporaryFilesRoot);
@@ -82,7 +81,7 @@ async function runShellAppBuilder(
outputFile: outputFilePath,
});
} catch (err) {
commonUtils.logErrorOnce(err, logger);
commonUtils.logErrorOnce(err);
throw err;
} finally {
if (!config.builder.skipCleanup) {
+1 -4
View File
@@ -1,10 +1,7 @@
import { IJob, IJobResult } from 'turtle/job';
import buildAndroid from './android';
import buildIOS from './ios';
const builders: { [index: string]: (job: IJob, logger: any) => Promise<IJobResult>} = {
export default {
ios: buildIOS,
android: buildAndroid,
};
export default builders;
+6 -5
View File
@@ -7,6 +7,7 @@ import * as fileUtils from 'turtle/builders/utils/file';
import * as keychain from 'turtle/builders/utils/ios/keychain';
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;
@@ -18,7 +19,7 @@ export default async function buildArchive(ctx: IContext, job: IJob) {
const manifest = await runShellAppBuilder(ctx, job);
await buildAndSignIPA(ctx, job, keychainInfo.path, manifest);
} catch (err) {
logErrorOnce(err, ctx.logger);
logErrorOnce(err);
throw err;
} finally {
if (keychainInfo) {
@@ -28,8 +29,8 @@ export default async function buildArchive(ctx: IContext, job: IJob) {
}
async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, manifest: any) {
ctx.logger = ctx.logger.child({ buildPhase: 'building and signing IPA' });
ctx.logger.info('building and signing IPA');
const l = logger.withFields({ buildPhase: 'building and signing IPA' });
l.info('building and signing IPA');
const {
credentials: { provisioningProfile, certPassword, teamId },
@@ -41,7 +42,7 @@ async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, m
const { provisioningProfilePath } = ctx;
await fileUtils.writeBase64ToBinaryFile(provisioningProfilePath, provisioningProfile as string);
ctx.logger.info('saved provisioning profile to temporary path');
l.info('saved provisioning profile to temporary path');
const ipaBuilder = createIPABuilder({
keychainPath,
@@ -56,5 +57,5 @@ async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, m
});
await ipaBuilder.build();
ctx.logger.info(`done building and signing IPA`);
l.info(`done building and signing IPA`);
}
+1 -3
View File
@@ -15,7 +15,6 @@ export interface IContext {
baseArchiveDir: string;
buildDir: string;
fakeUploadBuildPath?: string;
logger: any;
outputPath: string;
provisioningProfilePath: string;
provisioningProfileDir: string;
@@ -30,7 +29,7 @@ export interface IContext {
const { EXPOKIT_APP, EXPONENT_APP } = IosShellApp;
const { BUILD_TYPES } = IOS;
export function createBuilderContext(job: IJob, logger: any): IContext {
export function createBuilderContext(job: IJob): IContext {
const { join } = path;
const appUUID = uuidv4();
const {
@@ -48,7 +47,6 @@ export function createBuilderContext(job: IJob, logger: any): IContext {
appDir: join(config.directories.temporaryFilesRoot, appUUID),
appUUID,
workingDir,
logger,
};
context.buildDir = join(context.appDir, 'build');
context.provisioningProfileDir = join(context.appDir, 'provisioning');
+4 -4
View File
@@ -14,12 +14,12 @@ import { IJob, IJobResult } from 'turtle/job';
const { BUILD_TYPES } = IOS;
export default async function iosBuilder(job: IJob, logger: any): Promise<IJobResult> {
export default async function iosBuilder(job: IJob): Promise<IJobResult> {
if (job.config.buildType !== BUILD_TYPES.CLIENT) {
await ensureCanBuildSdkVersion(job);
}
const ctx = createBuilderContext(job, logger);
const ctx = createBuilderContext(job);
try {
await initBuilder(ctx);
@@ -27,7 +27,7 @@ export default async function iosBuilder(job: IJob, logger: any): Promise<IJobRe
const { buildType } = job.config;
if (buildType === BUILD_TYPES.CLIENT) {
await prepareAdHocBuildCredentials(job, ctx.logger);
await prepareAdHocBuildCredentials(job);
}
if ([BUILD_TYPES.ARCHIVE, BUILD_TYPES.CLIENT].includes(buildType!)) {
@@ -41,7 +41,7 @@ export default async function iosBuilder(job: IJob, logger: any): Promise<IJobRe
const artifactUrl = await uploadBuildToS3(ctx);
return { artifactUrl };
} catch (err) {
logErrorOnce(err, ctx.logger);
logErrorOnce(err);
throw err;
} finally {
await cleanup(ctx);
+4 -3
View File
@@ -1,10 +1,11 @@
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) {
ctx.logger = ctx.logger.child({ buildPhase: 'running simulator builder' });
ctx.logger.info('running simulator build');
const l = logger.withFields({ buildPhase: 'running simulator builder' });
l.info('running simulator build');
await runShellAppBuilder(ctx, job);
ctx.logger.info('build complete');
l.info('build complete');
}
+4 -3
View File
@@ -7,11 +7,12 @@ import { Credentials } 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';
async function getOrCreateCredentials(jobData: IJob, logger: any): Promise<IAndroidCredentials> {
async function getOrCreateCredentials(jobData: IJob): Promise<IAndroidCredentials> {
if (!jobData.credentials) {
const l = logger.child({ buildPhase: 'generating keystore' });
const l = logger.withFields({ buildPhase: 'generating keystore' });
const credentials: any = {};
l.info('Creating keystore');
const keystoreFilename = `/tmp/keystore-${jobData.id}.tmp.jks`;
@@ -43,7 +44,7 @@ async function getOrCreateCredentials(jobData: IJob, logger: any): Promise<IAndr
} else {
await sqs.sendMessage(jobData.id, UPDATE_CREDENTIALS, {
...credentials,
}, l);
});
l.info('Keystore sent to storage successfully');
}
await fs.unlink(keystoreFilename);
+2 -1
View File
@@ -2,6 +2,7 @@ import * as url from 'url';
import config from 'turtle/config';
import { IJob } from 'turtle/job';
import logger from 'turtle/logger';
export function getExperienceUrl(job: IJob) {
const { experienceName, config: jobConfig } = job;
@@ -17,7 +18,7 @@ export function getExperienceUrl(job: IJob) {
const alreadyLoggedError = Symbol('alreadyLoggedError');
export function logErrorOnce(err: any, logger: any) {
export function logErrorOnce(err: any) {
if (!err[alreadyLoggedError]) {
logger.error(err.stack);
err[alreadyLoggedError] = true;
+3 -2
View File
@@ -9,12 +9,13 @@ 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, logger: any) {
async function prepareAdHocBuildCredentials(job: IJob) {
if (process.platform !== 'darwin') {
throw new Error('This function should be called only on macOS!');
}
@@ -58,7 +59,7 @@ async function prepareAdHocBuildCredentials(job: IJob, logger: any) {
await sqs.sendMessage(job.id, UPDATE_CREDENTIALS, {
provisioningProfileId: credentials.provisioningProfileId,
provisioningProfile: credentials.provisioningProfile,
}, logger);
});
logger.info('Ad Hoc provisioning profile sent to storage successfully');
}
} catch (e) {
+4 -3
View File
@@ -2,13 +2,14 @@ import { IosKeychain } from 'xdl';
import { IContext } from 'turtle/builders/ios/context';
import * as fileUtils from 'turtle/builders/utils/file';
import logger from 'turtle/logger';
interface IKeychain {
path: string;
}
export async function create(ctx: IContext): Promise<IKeychain> {
const l = ctx.logger.child({ buildPhase: 'creating keychain' });
const l = logger.withFields({ buildPhase: 'creating keychain' });
try {
l.info('creating keychain...');
const keychainInfo = await IosKeychain.createKeychain(ctx.appUUID, false);
@@ -21,7 +22,7 @@ export async function create(ctx: IContext): Promise<IKeychain> {
}
export async function remove(ctx: IContext, keychainPath: string) {
const l = ctx.logger.child({ buildPhase: 'deleting keychain' });
const l = logger.withFields({ buildPhase: 'deleting keychain' });
try {
l.info('delete keychain...');
const keychainInfo = await IosKeychain.deleteKeychain({
@@ -48,7 +49,7 @@ export async function importCert(
certPassword,
}: { keychainPath: string; certP12: string; certPassword: string },
) {
const l = ctx.logger.child({ buildPhase: 'importing certificate into keychain' });
const l = logger.withFields({ buildPhase: 'importing certificate into keychain' });
try {
l.info('importing distribution certificate into keychain...');
const { tempCertPath: certPath } = ctx;
+3 -2
View File
@@ -10,6 +10,7 @@ import * as commonUtils from 'turtle/builders/utils/common';
import * as imageHelpers from 'turtle/builders/utils/image';
import { IOS } from 'turtle/constants/index';
import { IJob } from 'turtle/job';
import logger from 'turtle/logger/index';
const copyAsync = util.promisify(copy);
@@ -20,7 +21,7 @@ export default async function runShellAppBuilder(ctx: IContext, job: IJob): Prom
await copyAsync(ctx.applicationFilesSrc, ctx.baseArchiveDir);
ctx.logger.info(
logger.info(
{ buildPhase: 'icons setup' },
'ImageUtils: setting image functions to alternative sharp implementations',
);
@@ -48,6 +49,6 @@ export default async function runShellAppBuilder(ctx: IContext, job: IJob): Prom
});
}
ctx.logger.info({ buildPhase: 'configuring NSBundle' }, 'configuring NSBundle...');
logger.info({ buildPhase: 'configuring NSBundle' }, 'configuring NSBundle...');
return await IosShellApp.configureAndCopyArchiveAsync(shellAppParams);
}
+3 -3
View File
@@ -2,24 +2,24 @@ import * as fs from 'fs-extra';
import { uploadFile } from 'turtle/aws/s3';
import config from 'turtle/config';
import logger from 'turtle/logger';
interface IUploadCtx {
fakeUploadBuildPath?: string;
s3FileKey?: string;
uploadPath: string;
logger: any;
}
export async function uploadBuildToS3(ctx: IUploadCtx) {
if (config.builder.fakeUpload) {
const l = ctx.logger.child({ buildPhase: 'copying build artifact' });
const l = logger.withFields({ buildPhase: 'copying build artifact' });
const { fakeUploadBuildPath, uploadPath } = ctx;
l.info('copying build to fake upload directory');
await fs.copy(uploadPath, fakeUploadBuildPath as string);
l.info(`copied build to ${fakeUploadBuildPath}`);
return fakeUploadBuildPath as string;
} else {
const l = ctx.logger.child({ buildPhase: 'uploading to S3' });
const l = logger.withFields({ buildPhase: 'uploading to S3' });
l.info('uploading build artifact to S3');
const { Location: fileLocation } = await uploadFile({
key: ctx.s3FileKey as string,
+30 -30
View File
@@ -6,6 +6,7 @@ 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, turtleVersion } from 'turtle/turtleContext';
@@ -13,35 +14,35 @@ import { getPriorities } from 'turtle/utils/priorities';
import * as redis from 'turtle/utils/redis';
import { sanitizeJob } from 'turtle/validator';
function _maybeExit(logger: any) {
function _maybeExit() {
if (checkShouldExit()) {
logger.warn('Exiting due to previously received termination signal.');
process.exit();
}
}
export async function doJob(logger: any) {
const jobData = await getJob(logger);
await processJob(jobData, logger);
_maybeExit(logger);
export async function doJob() {
const jobData = await getJob();
await processJob(jobData);
_maybeExit();
}
export async function getJob(logger: any) {
_maybeExit(logger);
export async function getJob() {
_maybeExit();
logger.info('Fetching job');
while (true) {
try {
let job = null;
const priorities = await getPriorities(logger);
const priorities = await getPriorities();
for (const priority of priorities) {
job = await sqs.receiveMessage(priority, logger);
_maybeExit(logger);
job = await sqs.receiveMessage(priority);
_maybeExit();
if (job != null) {
job.priority = priority;
break;
}
}
_maybeExit(logger);
_maybeExit();
if (job !== null) {
return job;
}
@@ -51,30 +52,30 @@ export async function getJob(logger: any) {
}
}
async function processJob(jobData: any, logger: any) {
async function processJob(jobData: any) {
const receiptHandle = jobData.ReceiptHandle;
const { priority } = jobData;
let timeoutId;
try {
const rawJob = JSON.parse(jobData.Body);
timeoutId = failAfterMaxJobTime(priority, receiptHandle, rawJob, logger);
timeoutId = failAfterMaxJobTime(priority, receiptHandle, rawJob);
const job = await sanitizeJob(rawJob);
setCurrentJobId(job.id);
const pingerHandle = sqs.changeMessageVisibilityRecurring(priority, jobData.ReceiptHandle, job.id, logger);
const pingerHandle = sqs.changeMessageVisibilityRecurring(priority, jobData.ReceiptHandle, job.id);
logger.info(`Doing job MessageId=${jobData.MessageId} BuildId=${job.id} ${Date.now()}`);
const cancelled = await redis.checkIfCancelled(job.id, logger);
const cancelled = await redis.checkIfCancelled(job.id);
if (cancelled) {
logger.info('The job has been cancelled');
} else {
redis.registerListener(job.id, () => deleteMessage(priority, receiptHandle, logger), logger);
redis.registerListener(job.id, () => deleteMessage(priority, receiptHandle));
const startTimestamp = Date.now();
let buildFailed = false;
const buildType = _.get(job, 'config.buildType', 'default');
try {
const status = await build(job, logger);
const status = await build(job);
buildStatusMetric.add(buildType, status);
buildFailed = !status;
} catch (err) {
@@ -92,13 +93,13 @@ async function processJob(jobData: any, logger: any) {
);
buildDurationMetric.addTotalDuration(buildType, totalBuildDurationSecs, !buildFailed);
}
redis.unregisterListeners(logger);
redis.unregisterListeners();
}
logger.info(`Job done MessageId=${jobData.MessageId} BuildId=${job.id} ${Date.now()}`);
}
clearInterval(pingerHandle);
} finally {
await deleteMessage(priority, receiptHandle, logger);
await deleteMessage(priority, receiptHandle);
if (timeoutId) {
logger.info('Clearing job failer timeout...');
clearTimeout(timeoutId);
@@ -106,11 +107,11 @@ async function processJob(jobData: any, logger: any) {
}
}
function failAfterMaxJobTime(priority: string, receiptHandle: string, job: any, logger: any) {
function failAfterMaxJobTime(priority: string, receiptHandle: string, job: any) {
return setTimeout(async () => {
try {
sqs.sendMessage(job.id, BUILD.JOB_STATES.ERRORED, { turtleVersion }, logger);
await deleteMessage(priority, receiptHandle, logger);
sqs.sendMessage(job.id, BUILD.JOB_STATES.ERRORED, { turtleVersion });
await deleteMessage(priority, receiptHandle);
} finally {
logger.error('Going to terminate turtle agent, just in case...');
process.exit(1);
@@ -118,27 +119,26 @@ function failAfterMaxJobTime(priority: string, receiptHandle: string, job: any,
}, config.builder.maxJobTimeMs);
}
async function build(job: any, logger: any) {
async function build(job: any) {
const startTimestamp = Date.now();
const s3Url = await logger.init(job);
const buildLogger = logger.child({ jobID: job.id, experienceName: job.experienceName });
const calculateBuildDuration = () => Math.ceil((Date.now() - startTimestamp) / 1000);
try {
await sqs.sendMessage(job.id, BUILD.JOB_STATES.IN_PROGRESS, {
logUrl: s3Url,
logFormat: 'json',
}, buildLogger);
const result = await builders[job.platform](job, buildLogger);
});
const result = await (builders as any)[job.platform](job);
const buildDuration = calculateBuildDuration();
sqs.sendMessage(job.id, BUILD.JOB_STATES.FINISHED, {
...result,
turtleVersion,
buildDuration,
}, buildLogger);
});
return true;
} catch (err) {
logErrorOnce(err, buildLogger);
logErrorOnce(err);
const buildDuration = calculateBuildDuration();
let reason;
if (err instanceof BuildError) {
@@ -148,14 +148,14 @@ async function build(job: any, logger: any) {
turtleVersion,
buildDuration,
...reason && { reason },
}, buildLogger);
});
return false;
} finally {
await logger.cleanup(job);
}
}
async function deleteMessage(priority: string, receiptHandle: string, logger: any) {
async function deleteMessage(priority: string, receiptHandle: string) {
try {
setCurrentJobId(null);
await sqs.deleteMessage(priority, receiptHandle);
+10
View File
@@ -4,6 +4,7 @@ import { SentryStream } from 'bunyan-sentry-stream';
import { Client as RavenClient } from 'raven';
import config from 'turtle/config';
import * as constants from 'turtle/constants/logger';
import { IJob } from 'turtle/job';
import S3Stream from 'turtle/logger/s3Stream';
import { isOffline } from 'turtle/turtleContext';
@@ -69,6 +70,8 @@ const logger = bunyan.createLogger({
streams,
});
logger.withFields = (extraFields: any) => withFields(logger, extraFields);
logger.init = async (job: IJob) => {
return await s3logger.init(job);
};
@@ -80,3 +83,10 @@ logger.cleanup = async () => {
};
export default logger;
export function withFields(loggerObj: any, extraFields: any) {
return constants.LEVELS.reduce((obj, level) => {
obj[level] = (...args: any[]) => loggerObj[level](extraFields, ...args);
return obj;
}, {} as any);
}
+2 -1
View File
@@ -1,10 +1,11 @@
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';
export function register(logger: any) {
export function register() {
for (const metricName of [TURTLE_BUILD_DURATION_METRIC_NAME, TOTAL_BUILD_DURATION_METRIC_NAME]) {
logger.info(`Registered AWS Cloudwatch metric: ${metricName}`);
cloudwatch.registerMetric({
+2 -1
View File
@@ -2,10 +2,11 @@ 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';
const BUILD_METRIC_NAME = 'build';
export function register(logger: any) {
export function register() {
logger.info(`Registered AWS Cloudwatch metric: ${BUILD_METRIC_NAME}`);
cloudwatch.registerMetric({
metricName: BUILD_METRIC_NAME,
+4 -3
View File
@@ -1,11 +1,12 @@
import * as cloudwatch from 'turtle/aws/cloudwatch';
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: any) {
export async function init() {
logger.info('Initializing Cloudwatch metrics');
await cloudwatch.init(logger);
metricsToRegister.forEach((metric) => metric.register(logger));
await cloudwatch.init();
metricsToRegister.forEach((metric) => metric.register());
}
+3 -3
View File
@@ -31,11 +31,11 @@ async function main() {
);
LoggerDetach.configure(logger);
if (setup[config.platform]) {
await setup[config.platform](logger);
await setup[config.platform]();
}
try {
const redis = await getRedisClient(logger, RedisClient.Configuration);
const redis = await getRedisClient(RedisClient.Configuration);
await redis.set(REDIS_TURTLE_VERSION_KEY, turtleVersion);
logger.info(`Register Turtle version ${turtleVersion} in Redis`);
} catch (err) {
@@ -44,7 +44,7 @@ async function main() {
while (true) {
try {
await doJob(logger);
await doJob();
} catch (err) {
logger.error({ err }, 'Failed to do a job');
}
+3 -2
View File
@@ -1,7 +1,8 @@
import logger from 'turtle/logger';
import commonSetup from 'turtle/setup/common';
export default async function setup(logger: any) {
export default async function setup() {
logger.info('Setting up environment...');
await commonSetup(logger);
await commonSetup();
logger.info('Finished setting up environment');
}
+2 -2
View File
@@ -3,8 +3,8 @@ import fs from 'fs-extra';
import config from 'turtle/config';
import * as metrics from 'turtle/metrics';
export default async function commonSetup(logger: any) {
await metrics.init(logger);
export default async function commonSetup() {
await metrics.init();
await fs.remove(config.directories.temporaryFilesRoot);
await fs.remove(config.directories.tempS3LogsDir);
}
+3 -2
View File
@@ -3,6 +3,7 @@ import os from 'os';
import path from 'path';
import * as keychain from 'turtle/builders/utils/ios/keychain';
import logger from 'turtle/logger';
import commonSetup from 'turtle/setup/common';
async function deleteProvisioningProfilesFromHomedir() {
@@ -16,10 +17,10 @@ async function deleteProvisioningProfilesFromHomedir() {
}
}
export default async function setup(logger: any) {
export default async function setup() {
logger.info('Setting up environment...');
await deleteProvisioningProfilesFromHomedir();
await keychain.cleanUp();
await commonSetup(logger);
await commonSetup();
logger.info('Finished setting up environment');
}
+2 -1
View File
@@ -2,6 +2,7 @@ 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';
@@ -39,7 +40,7 @@ export function createDefaultConfigurationKey(platform = config.platform) {
return `${configPrefix(platform)}:default`;
}
export async function getPriorities(logger: any) {
export async function getPriorities() {
if (config.env === 'test') {
return NORMAL_CONFIGURATION;
}
+7 -6
View File
@@ -1,6 +1,7 @@
import Redis from 'ioredis';
import config from 'turtle/config';
import logger from 'turtle/logger';
const MILLIS_TO_UPLOAD_LOGS = 3000;
export const MILLIS_CONNECTION_TIMEOUT = 10000;
@@ -46,7 +47,7 @@ const redisClients: IRedisClients = {
[RedisClient.Configuration]: null,
};
export async function getRedisClient(logger: any, type = RedisClient.Default) {
export async function getRedisClient(type = RedisClient.Default) {
if (!redisClients[type]) {
try {
redisClients[type] = await connect(MILLIS_CONNECTION_TIMEOUT, type);
@@ -57,9 +58,9 @@ export async function getRedisClient(logger: any, type = RedisClient.Default) {
return redisClients[type];
}
export async function checkIfCancelled(jobId: string, logger: any) {
export async function checkIfCancelled(jobId: string) {
try {
const redis = await getRedisClient(logger);
const redis = await getRedisClient();
return await redis.get(`jobs:cancelled:${jobId}`);
} catch (err) {
if (config.deploymentEnv === 'development') {
@@ -70,7 +71,7 @@ export async function checkIfCancelled(jobId: string, logger: any) {
}
}
export async function registerListener(jobId: string, deleteMessage: any, logger: any) {
export async function registerListener(jobId: string, deleteMessage: any) {
try {
const redis = await getRedisClient(RedisClient.Subscriber);
redis.subscribe('jobs:cancelled');
@@ -87,9 +88,9 @@ export async function registerListener(jobId: string, deleteMessage: any, logger
}
}
export async function unregisterListeners(logger: any) {
export async function unregisterListeners() {
try {
const redis = await getRedisClient(logger);
const redis = await getRedisClient();
redis.removeAllListeners('message');
} catch (err) {
logger.error(err);
-3
View File
@@ -1,3 +0,0 @@
#! /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
@@ -1,3 +0,0 @@
#! /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
@@ -1,3 +0,0 @@
#! /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
@@ -1,3 +0,0 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../nix" yarn --command yarn "$@"
-3
View File
@@ -1,3 +0,0 @@
#! /usr/bin/env bash
# This file was generated by nix-binstubs
exec nix run -f "$(dirname "$0")/../../nix" yarn --command yarnpkg "$@"
+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 "$@"
+8 -28
View File
@@ -346,13 +346,12 @@
pify "^4.0.0"
retry-request "^4.0.0"
"@google-cloud/logging-bunyan@^0.10.1":
version "0.10.1"
resolved "https://registry.yarnpkg.com/@google-cloud/logging-bunyan/-/logging-bunyan-0.10.1.tgz#bdb234fec749d41eff7f4dc0d2d3c6b9396438fe"
integrity sha512-sJyc8Smb3SBL+XNe4hjzg02Mb+fH03dxCVfdKg5xoUCnNMPIVc7l4yPLPyoK5ghfBkvICQk7jEpeQ7gDIX6r4w==
"@google-cloud/logging-bunyan@^0.9.5":
version "0.9.5"
resolved "https://registry.yarnpkg.com/@google-cloud/logging-bunyan/-/logging-bunyan-0.9.5.tgz#462f41a6266d307e08e656a6d97a914ab13cdc6b"
integrity sha512-zhXbSkyJlnuH3Fgc7Q4tN1J75KR14OkzTAT9hFNthJz1dvfFsXfgwXdhMvCydK46UAxwk35nNsDg8MrxqLMQlw==
dependencies:
"@google-cloud/logging" "^4.1.0"
google-auth-library "^3.1.0"
"@google-cloud/logging@^4.1.0":
version "4.4.0"
@@ -4689,6 +4688,8 @@ extend-shallow@^2.0.0, extend-shallow@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
dependencies:
is-extendable "^0.1.0"
extend-shallow@^3.0.0, extend-shallow@^3.0.2:
version "3.0.2"
@@ -5309,14 +5310,6 @@ gcp-metadata@^0.9.0, gcp-metadata@^0.9.3:
gaxios "^1.0.2"
json-bigint "^0.3.0"
gcp-metadata@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-1.0.0.tgz#5212440229fa099fc2f7c2a5cdcb95575e9b2ca6"
integrity sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ==
dependencies:
gaxios "^1.0.2"
json-bigint "^0.3.0"
genfun@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537"
@@ -5689,21 +5682,6 @@ google-auth-library@^3.0.0:
lru-cache "^5.0.0"
semver "^5.5.0"
google-auth-library@^3.1.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-3.1.2.tgz#ff2f88cd5cd2118a57bd3d5ad3c093c8837fc350"
integrity sha512-cDQMzTotwyWMrg5jRO7q0A4TL/3GWBgO7I7q5xGKNiiFf9SmGY/OJ1YsLMgI2MVHHsEGyrqYnbnmV1AE+Z6DnQ==
dependencies:
base64-js "^1.3.0"
fast-text-encoding "^1.0.0"
gaxios "^1.2.1"
gcp-metadata "^1.0.0"
gtoken "^2.3.2"
https-proxy-agent "^2.2.1"
jws "^3.1.5"
lru-cache "^5.0.0"
semver "^5.5.0"
google-gax@^0.25.0:
version "0.25.2"
resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-0.25.2.tgz#612bf6dc9e2cd42d1f3294def0b003f7452cf5fb"
@@ -11495,6 +11473,8 @@ split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
dependencies:
extend-shallow "^3.0.0"
split2@^2.0.0:
version "2.2.0"