mirror of
https://github.com/expo/turtle.git
synced 2026-11-14 19:12:55 -10:00
Compare commits
4
Commits
v0.23.3
...
log-repairs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0da20f4ab5 | ||
|
|
05d55bf731 | ||
|
|
074ba521f9 | ||
|
|
451999f0b9 |
No files matched your search
+1
-1
@@ -115,7 +115,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/spawn-async": "^1.4.0",
|
||||
"@google-cloud/logging-bunyan": "^0.9.5",
|
||||
"@google-cloud/logging-bunyan": "^0.10.1",
|
||||
"async-retry": "^1.2.1",
|
||||
"aws-sdk": "^2.226.1",
|
||||
"bunyan": "^1.8.12",
|
||||
|
||||
@@ -34,7 +34,7 @@ async function performSmokeTest(recording: string) {
|
||||
});
|
||||
}
|
||||
|
||||
await jobManager.doJob();
|
||||
await jobManager.doJob(logger);
|
||||
if (!fixtureExists) {
|
||||
await fs.writeFile(recording, JSON.stringify(nock.recorder.play()));
|
||||
}
|
||||
|
||||
+19
-18
@@ -11,7 +11,6 @@ import {
|
||||
MetricRegistrationObject,
|
||||
} from 'turtle/aws/cloudwatch/types';
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
interface IState {
|
||||
metrics: IMetric[];
|
||||
@@ -38,12 +37,12 @@ const cloudWatch = new aws.CloudWatch({
|
||||
secretAccessKey: config.aws.secretAccessKey,
|
||||
});
|
||||
|
||||
export function init() {
|
||||
export function init(logger: any) {
|
||||
if (config.cloudwatch.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.intervalId = setInterval(_dumpMetrics, config.cloudwatch.intervalMs);
|
||||
state.intervalId = setInterval(_dumpMetrics(logger), config.cloudwatch.intervalMs);
|
||||
}
|
||||
|
||||
export function addMetricData({
|
||||
@@ -111,23 +110,25 @@ function _calculateGroupHash(metricName: string, extraDimensions: any[]) {
|
||||
return `${metricName}${dimString}`;
|
||||
}
|
||||
|
||||
function _dumpMetrics() {
|
||||
if (!state.metrics.length && Object.keys(state.registeredMetrics).length < 0) {
|
||||
// empty metrics list, skipping
|
||||
return;
|
||||
}
|
||||
function _dumpMetrics(logger: any) {
|
||||
return () => {
|
||||
if (!state.metrics.length && Object.keys(state.registeredMetrics).length < 0) {
|
||||
// empty metrics list, skipping
|
||||
return;
|
||||
}
|
||||
|
||||
logger.trace(
|
||||
'[cloudwatch] lenghts for state.metrics=%d, state.registeredMetrics=%d',
|
||||
state.metrics.length,
|
||||
Object.keys(state.registeredMetrics).length,
|
||||
);
|
||||
logger.trace(
|
||||
'[cloudwatch] lengths for state.metrics=%d, state.registeredMetrics=%d',
|
||||
state.metrics.length,
|
||||
Object.keys(state.registeredMetrics).length,
|
||||
);
|
||||
|
||||
_addEmptyMetrics(state.metrics);
|
||||
const formattedMetrics = _formatMetrics(state.metrics);
|
||||
_pushMetrics(formattedMetrics);
|
||||
_addEmptyMetrics(state.metrics);
|
||||
const formattedMetrics = _formatMetrics(state.metrics);
|
||||
_pushMetrics(formattedMetrics, logger);
|
||||
|
||||
state.metrics = [];
|
||||
state.metrics = [];
|
||||
};
|
||||
}
|
||||
|
||||
function _addEmptyMetrics(metrics: IMetric[]) {
|
||||
@@ -174,7 +175,7 @@ function _reduceMetrics(metrics: IMetric[]) {
|
||||
);
|
||||
}
|
||||
|
||||
function _pushMetrics(metrics: IMetricsChunk[]) {
|
||||
function _pushMetrics(metrics: IMetricsChunk[], logger: any) {
|
||||
logger.trace('[cloudwatch] pushing metrics', JSON.stringify(metrics, null, '\t'));
|
||||
metrics.forEach((metricsChunk) => {
|
||||
cloudWatch.putMetricData(metricsChunk, (err) => {
|
||||
|
||||
+3
-4
@@ -5,7 +5,6 @@ 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({
|
||||
@@ -16,7 +15,7 @@ const sqs = new AWS.SQS({
|
||||
|
||||
const VISIBILITY_TIMEOUT_SEC = 30;
|
||||
|
||||
export async function receiveMessage(priority: string) {
|
||||
export async function receiveMessage(priority: string, logger: any) {
|
||||
const params = {
|
||||
MaxNumberOfMessages: 1,
|
||||
QueueUrl: QUEUE_URL(priority),
|
||||
@@ -64,7 +63,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) {
|
||||
export function changeMessageVisibilityRecurring(priority: string, receiptHandle: string, jobId: string, logger: any) {
|
||||
return setInterval(() => {
|
||||
if (getCurrentJobId() === jobId) {
|
||||
changeMessageVisibility(priority, receiptHandle).catch((err) => {
|
||||
@@ -74,7 +73,7 @@ export function changeMessageVisibilityRecurring(priority: string, receiptHandle
|
||||
}, (VISIBILITY_TIMEOUT_SEC * 1000) / 3);
|
||||
}
|
||||
|
||||
export async function sendMessage(jobId: string, status: JOB_UPDATE_TYPE, data = {}) {
|
||||
export async function sendMessage(jobId: string, status: JOB_UPDATE_TYPE, data = {}, logger: any) {
|
||||
const params = {
|
||||
MessageBody: JSON.stringify({ jobId, status, ...data }),
|
||||
QueueUrl: OUTPUT_QUEUE_URL(),
|
||||
|
||||
@@ -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 (program: any) => {
|
||||
return builder(program, setCommonCommandOptions);
|
||||
return (logger: any) => (program: any) => {
|
||||
return builder(program, setCommonCommandOptions, logger);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import builders from 'turtle/bin/commands/build';
|
||||
import setup from 'turtle/bin/commands/setup';
|
||||
|
||||
export const setupIOS = setup.ios;
|
||||
export const setupAndroid = setup.android;
|
||||
|
||||
export const buildIOS = builders.ios;
|
||||
export const buildAndroid = builders.android;
|
||||
export default {
|
||||
setupIOS: setup.ios,
|
||||
setupAndroid: setup.android,
|
||||
buildIOS: builders.ios,
|
||||
buildAndroid: builders.android,
|
||||
};
|
||||
@@ -4,9 +4,6 @@ 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) => {
|
||||
@@ -23,14 +20,16 @@ function createSetupCommand(platform: string, os?: string) {
|
||||
};
|
||||
}
|
||||
|
||||
async function setupAction(program: any, cmd: any, platform: string, os?: string) {
|
||||
async function setupAction(program: any, cmd: any, platform: string, logger: any, os?: string) {
|
||||
try {
|
||||
if (os && process.platform !== os) {
|
||||
throw new Error('We don\'t support running standalone app builds for this platform on your operating system');
|
||||
}
|
||||
|
||||
const { sdkVersion } = cmd;
|
||||
await setup(platform, sdkVersion);
|
||||
const l = logger.child({ buildPhase: 'setting up environment' });
|
||||
|
||||
await setup(platform, l, sdkVersion);
|
||||
l.info('it\'s all set!');
|
||||
} catch (err) {
|
||||
logger.error({ err }, `Failed to setup environment for ${platform} builds`);
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import program, { Command } from 'commander';
|
||||
import fs from 'fs-extra';
|
||||
import { LoggerDetach, ModuleVersion } from 'xdl';
|
||||
|
||||
import * as commands from 'turtle/bin/commands';
|
||||
import 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).forEach((command) => registerCommand(program, command));
|
||||
Object.values(commands).map((command) => command(logger)).forEach((command) => registerCommand(program, command));
|
||||
program.parse(process.argv);
|
||||
|
||||
const subCommand = process.argv[2];
|
||||
|
||||
@@ -12,7 +12,6 @@ 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[] = [
|
||||
@@ -40,25 +39,24 @@ const REQUIRED_TOOLS: IToolDefinition[] = [
|
||||
},
|
||||
},
|
||||
];
|
||||
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
|
||||
const l = logger.withFields(LOGGER_FIELDS);
|
||||
|
||||
export default async function setup(sdkVersion?: string) {
|
||||
await checkSystem(REQUIRED_TOOLS);
|
||||
await prepareAndroidEnv();
|
||||
export default async function setup(logger: any, sdkVersion?: string) {
|
||||
await checkSystem(REQUIRED_TOOLS, logger);
|
||||
await prepareAndroidEnv(logger);
|
||||
if (sdkVersion) {
|
||||
await ensureShellAppIsPresent(
|
||||
sdkVersion,
|
||||
{ formatShellAppDirectory, formatShellAppTarballUriPath },
|
||||
logger,
|
||||
_shellAppPostDownloadAction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function prepareAndroidEnv() {
|
||||
async function prepareAndroidEnv(logger: any) {
|
||||
await fs.ensureDir(config.directories.androidDependenciesDir);
|
||||
const sdkConfig = await ensureAndroidSDKIsPresent();
|
||||
const ndkConfig = await ensureAndroidNDKIsPresent();
|
||||
const sdkConfig = await ensureAndroidSDKIsPresent(logger);
|
||||
const ndkConfig = await ensureAndroidNDKIsPresent(logger);
|
||||
_setEnvVars({ ...sdkConfig.envVars, ...ndkConfig.envVars });
|
||||
_alterPath([...sdkConfig.path, ...sdkConfig.path]);
|
||||
}
|
||||
@@ -67,21 +65,23 @@ function formatShellAppTarballUriPath(sdkMajorVersion: string) {
|
||||
return path.join(config.directories.shellTarballsDir, PLATFORMS.ANDROID, `sdk${sdkMajorVersion}`);
|
||||
}
|
||||
|
||||
async function _shellAppPostDownloadAction(workingdir: string) {
|
||||
async function _shellAppPostDownloadAction(workingdir: string, logger: any) {
|
||||
const inWorkingdir = (filename: string) => path.join(workingdir, filename);
|
||||
|
||||
await fs.move(inWorkingdir('package.json'), inWorkingdir('exponent-package.json'));
|
||||
await fs.move(inWorkingdir('universe-package.json'), inWorkingdir('package.json'));
|
||||
await _installNodeModules(workingdir);
|
||||
await _installNodeModules(workingdir, logger);
|
||||
await fs.move(inWorkingdir('package.json'), inWorkingdir('universe-package.json'));
|
||||
await fs.move(inWorkingdir('exponent-package.json'), inWorkingdir('package.json'));
|
||||
|
||||
// TODO: remove following lines after upgrading android shell app
|
||||
const toolsPublicDir = path.join(workingdir, 'tools-public');
|
||||
await _installNodeModules(toolsPublicDir);
|
||||
await _installNodeModules(toolsPublicDir, logger);
|
||||
}
|
||||
|
||||
async function _installNodeModules(cwd: string) {
|
||||
async function _installNodeModules(cwd: string, logger: any) {
|
||||
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
|
||||
const l = logger.child(LOGGER_FIELDS);
|
||||
l.info(`installing dependencies in ${cwd} directory...`);
|
||||
const command = await _shouldUseYarnOrNpm();
|
||||
await ExponentTools.spawnAsyncThrowError(command, ['install'], {
|
||||
|
||||
@@ -7,20 +7,17 @@ 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`;
|
||||
|
||||
const LOGGER_FIELDS = { buildPhase: 'setting up environment' };
|
||||
const l = logger.withFields(LOGGER_FIELDS);
|
||||
|
||||
export default async function ensureAndroidNDKIsPresent() {
|
||||
export default async function ensureAndroidNDKIsPresent(logger: any) {
|
||||
const androidNdkDir = path.join(config.directories.androidDependenciesDir, 'ndk');
|
||||
if (!(await fs.pathExists(androidNdkDir))) {
|
||||
await fs.ensureDir(androidNdkDir);
|
||||
const androidNdkDownloadPath = formatArtifactDownloadPath(ANDROID_NDK_URL);
|
||||
|
||||
try {
|
||||
const l = logger.child({ buildPhase: 'setting up environment' });
|
||||
l.info('Downloading Android NDK');
|
||||
await fs.ensureDir(config.directories.artifactsDir);
|
||||
await download(ANDROID_NDK_URL, androidNdkDownloadPath);
|
||||
|
||||
@@ -8,20 +8,17 @@ 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' };
|
||||
const l = logger.withFields(LOGGER_FIELDS);
|
||||
|
||||
export default async function ensureAndroidSDKIsPresent() {
|
||||
export default async function ensureAndroidSDKIsPresent(logger: any) {
|
||||
const androidSdkDir = path.join(config.directories.androidDependenciesDir, 'sdk');
|
||||
if (!(await fs.pathExists(androidSdkDir))) {
|
||||
await fs.ensureDir(androidSdkDir);
|
||||
const androidSdkDownloadPath = formatArtifactDownloadPath(ANDROID_SDK_URL);
|
||||
|
||||
try {
|
||||
const l = logger.child(LOGGER_FIELDS);
|
||||
l.info('Downloading Android SDK');
|
||||
await fs.ensureDir(config.directories.artifactsDir);
|
||||
await download(ANDROID_SDK_URL, androidSdkDownloadPath);
|
||||
|
||||
@@ -34,13 +34,13 @@ const REQUIRED_TOOLS: IToolDefinition[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export default async function setup(sdkVersion?: string) {
|
||||
await checkSystem(REQUIRED_TOOLS);
|
||||
export default async function setup(logger: any, sdkVersion?: string) {
|
||||
await checkSystem(REQUIRED_TOOLS, logger);
|
||||
if (sdkVersion) {
|
||||
await ensureShellAppIsPresent(sdkVersion, {
|
||||
formatShellAppDirectory,
|
||||
formatShellAppTarballUriPath,
|
||||
});
|
||||
}, logger);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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, sdkVersion?: string) {
|
||||
export default async function setup(platform: string, logger: any, sdkVersion?: string) {
|
||||
if (sdkVersion && !isSemver(sdkVersion)) {
|
||||
throw new Error('SDK version is not valid.');
|
||||
}
|
||||
|
||||
if (platform === PLATFORMS.IOS) {
|
||||
return await setupIos(sdkVersion);
|
||||
return await setupIos(logger, sdkVersion);
|
||||
} else if (platform === PLATFORMS.ANDROID) {
|
||||
return await setupAndroid(sdkVersion);
|
||||
return await setupAndroid(logger, sdkVersion);
|
||||
} else {
|
||||
throw new Error('This should never happen :(');
|
||||
}
|
||||
|
||||
@@ -8,50 +8,54 @@ 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) => void;
|
||||
type PostDownloadAction = (workingdir: string, logger: any) => void;
|
||||
|
||||
export async function checkSystem(requiredTools: IToolDefinition[]) {
|
||||
await ensureToolsAreInstalled(requiredTools);
|
||||
export async function checkSystem(requiredTools: IToolDefinition[], logger: any) {
|
||||
await ensureToolsAreInstalled(requiredTools, logger);
|
||||
}
|
||||
|
||||
export async function ensureShellAppIsPresent(
|
||||
sdkVersion: string,
|
||||
formatters: IShellAppFormaters,
|
||||
logger: any,
|
||||
postDownloadAction?: PostDownloadAction,
|
||||
) {
|
||||
const workingdir = formatters.formatShellAppDirectory({ sdkVersion });
|
||||
const l = logger.child({ buildPhase: 'setting up environment' });
|
||||
if (await fs.pathExists(workingdir)) {
|
||||
return;
|
||||
}
|
||||
l.info(`shell app for SDK ${sdkVersion} doesn't exist, downloading...`);
|
||||
await _downloadShellApp(sdkVersion, workingdir, formatters);
|
||||
await _downloadShellApp(sdkVersion, workingdir, formatters, l);
|
||||
if (postDownloadAction) {
|
||||
await postDownloadAction(workingdir);
|
||||
await postDownloadAction(workingdir, logger);
|
||||
}
|
||||
}
|
||||
|
||||
async function _downloadShellApp(sdkVersion: string, targetDirectory: string, formatters: IShellAppFormaters) {
|
||||
async function _downloadShellApp(
|
||||
sdkVersion: string,
|
||||
targetDirectory: string,
|
||||
formatters: IShellAppFormaters,
|
||||
logger: any,
|
||||
) {
|
||||
const shellAppTarballS3Uri = await _readShellAppTarballS3Uri(sdkVersion, formatters);
|
||||
const tarballDownloadTargetPath = formatArtifactDownloadPath(shellAppTarballS3Uri);
|
||||
await fs.ensureDir(config.directories.artifactsDir);
|
||||
await download(shellAppTarballS3Uri, tarballDownloadTargetPath);
|
||||
l.info('shell app has been downloaded');
|
||||
logger.info('shell app has been downloaded');
|
||||
await fs.ensureDir(targetDirectory);
|
||||
l.info('extracting shell app (this may take a while)...');
|
||||
logger.info('extracting shell app (this may take a while)...');
|
||||
await tar.x({
|
||||
file: tarballDownloadTargetPath,
|
||||
C: targetDirectory,
|
||||
});
|
||||
l.info('shell app extracted');
|
||||
logger.info('shell app extracted');
|
||||
}
|
||||
|
||||
export function formatArtifactDownloadPath(uri: string) {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import util from 'util';
|
||||
import _which from 'which';
|
||||
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
const which = util.promisify(_which);
|
||||
const l = logger.withFields({ buildPhase: 'setting up environment' });
|
||||
|
||||
export interface IToolDefinition {
|
||||
command: string;
|
||||
@@ -12,7 +9,7 @@ export interface IToolDefinition {
|
||||
testFn?: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
export async function ensureToolsAreInstalled(tools: IToolDefinition[]) {
|
||||
export async function ensureToolsAreInstalled(tools: IToolDefinition[], logger: any) {
|
||||
let isAnyToolMissing = false;
|
||||
for (const { command, missingDescription, testFn } of tools) {
|
||||
try {
|
||||
@@ -24,6 +21,7 @@ export async function ensureToolsAreInstalled(tools: IToolDefinition[]) {
|
||||
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`);
|
||||
|
||||
@@ -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, sdkVersion);
|
||||
await setup(platform, logger, sdkVersion);
|
||||
const credentials = await prepareCredentials(cmd);
|
||||
const rawJob = {
|
||||
...buildJobObject(appJSON, args, credentials),
|
||||
|
||||
@@ -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): Promise<IJobResult> {
|
||||
export default async function buildAndroid(jobData: IJob, logger: any): Promise<IJobResult> {
|
||||
await ensureCanBuildSdkVersion(jobData);
|
||||
const credentials = await getOrCreateCredentials(jobData);
|
||||
const apkFilePath = await runShellAppBuilder(jobData, credentials);
|
||||
const credentials = await getOrCreateCredentials(jobData, logger);
|
||||
const apkFilePath = await runShellAppBuilder(jobData, credentials, logger);
|
||||
const randomHex = uuidv4().replace(/-/g, '');
|
||||
const s3Filename = `${jobData.experienceName}-${randomHex}-signed.apk`;
|
||||
const s3FileKey = `android/${s3Filename}`;
|
||||
const fakeUploadFilename = s3Filename.replace('/', '\\');
|
||||
const artifactUrl = await uploadBuildToS3({
|
||||
logger,
|
||||
uploadPath: apkFilePath,
|
||||
s3FileKey,
|
||||
...config.builder.fakeUpload && {
|
||||
@@ -40,6 +40,7 @@ export default async function buildAndroid(jobData: IJob): Promise<IJobResult> {
|
||||
async function runShellAppBuilder(
|
||||
jobData: IJob,
|
||||
credentials: IAndroidCredentials,
|
||||
logger: any,
|
||||
): Promise<string> {
|
||||
const { temporaryFilesRoot } = config.directories;
|
||||
await fs.ensureDir(temporaryFilesRoot);
|
||||
@@ -81,7 +82,7 @@ async function runShellAppBuilder(
|
||||
outputFile: outputFilePath,
|
||||
});
|
||||
} catch (err) {
|
||||
commonUtils.logErrorOnce(err);
|
||||
commonUtils.logErrorOnce(err, logger);
|
||||
throw err;
|
||||
} finally {
|
||||
if (!config.builder.skipCleanup) {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { IJob, IJobResult } from 'turtle/job';
|
||||
import buildAndroid from './android';
|
||||
import buildIOS from './ios';
|
||||
|
||||
export default {
|
||||
const builders: { [index: string]: (job: IJob, logger: any) => Promise<IJobResult>} = {
|
||||
ios: buildIOS,
|
||||
android: buildAndroid,
|
||||
};
|
||||
|
||||
export default builders;
|
||||
@@ -7,7 +7,6 @@ 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;
|
||||
@@ -19,7 +18,7 @@ export default async function buildArchive(ctx: IContext, job: IJob) {
|
||||
const manifest = await runShellAppBuilder(ctx, job);
|
||||
await buildAndSignIPA(ctx, job, keychainInfo.path, manifest);
|
||||
} catch (err) {
|
||||
logErrorOnce(err);
|
||||
logErrorOnce(err, ctx.logger);
|
||||
throw err;
|
||||
} finally {
|
||||
if (keychainInfo) {
|
||||
@@ -29,8 +28,8 @@ export default async function buildArchive(ctx: IContext, job: IJob) {
|
||||
}
|
||||
|
||||
async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, manifest: any) {
|
||||
const l = logger.withFields({ buildPhase: 'building and signing IPA' });
|
||||
l.info('building and signing IPA');
|
||||
ctx.logger = ctx.logger.child({ buildPhase: 'building and signing IPA' });
|
||||
ctx.logger.info('building and signing IPA');
|
||||
|
||||
const {
|
||||
credentials: { provisioningProfile, certPassword, teamId },
|
||||
@@ -42,7 +41,7 @@ async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, m
|
||||
|
||||
const { provisioningProfilePath } = ctx;
|
||||
await fileUtils.writeBase64ToBinaryFile(provisioningProfilePath, provisioningProfile as string);
|
||||
l.info('saved provisioning profile to temporary path');
|
||||
ctx.logger.info('saved provisioning profile to temporary path');
|
||||
|
||||
const ipaBuilder = createIPABuilder({
|
||||
keychainPath,
|
||||
@@ -57,5 +56,5 @@ async function buildAndSignIPA(ctx: IContext, job: IJob, keychainPath: string, m
|
||||
});
|
||||
await ipaBuilder.build();
|
||||
|
||||
l.info(`done building and signing IPA`);
|
||||
ctx.logger.info(`done building and signing IPA`);
|
||||
}
|
||||
@@ -15,6 +15,7 @@ export interface IContext {
|
||||
baseArchiveDir: string;
|
||||
buildDir: string;
|
||||
fakeUploadBuildPath?: string;
|
||||
logger: any;
|
||||
outputPath: string;
|
||||
provisioningProfilePath: string;
|
||||
provisioningProfileDir: string;
|
||||
@@ -29,7 +30,7 @@ export interface IContext {
|
||||
const { EXPOKIT_APP, EXPONENT_APP } = IosShellApp;
|
||||
const { BUILD_TYPES } = IOS;
|
||||
|
||||
export function createBuilderContext(job: IJob): IContext {
|
||||
export function createBuilderContext(job: IJob, logger: any): IContext {
|
||||
const { join } = path;
|
||||
const appUUID = uuidv4();
|
||||
const {
|
||||
@@ -47,6 +48,7 @@ export function createBuilderContext(job: IJob): IContext {
|
||||
appDir: join(config.directories.temporaryFilesRoot, appUUID),
|
||||
appUUID,
|
||||
workingDir,
|
||||
logger,
|
||||
};
|
||||
context.buildDir = join(context.appDir, 'build');
|
||||
context.provisioningProfileDir = join(context.appDir, 'provisioning');
|
||||
|
||||
@@ -14,12 +14,12 @@ import { IJob, IJobResult } from 'turtle/job';
|
||||
|
||||
const { BUILD_TYPES } = IOS;
|
||||
|
||||
export default async function iosBuilder(job: IJob): Promise<IJobResult> {
|
||||
export default async function iosBuilder(job: IJob, logger: any): Promise<IJobResult> {
|
||||
if (job.config.buildType !== BUILD_TYPES.CLIENT) {
|
||||
await ensureCanBuildSdkVersion(job);
|
||||
}
|
||||
|
||||
const ctx = createBuilderContext(job);
|
||||
const ctx = createBuilderContext(job, logger);
|
||||
|
||||
try {
|
||||
await initBuilder(ctx);
|
||||
@@ -27,7 +27,7 @@ export default async function iosBuilder(job: IJob): Promise<IJobResult> {
|
||||
const { buildType } = job.config;
|
||||
|
||||
if (buildType === BUILD_TYPES.CLIENT) {
|
||||
await prepareAdHocBuildCredentials(job);
|
||||
await prepareAdHocBuildCredentials(job, ctx.logger);
|
||||
}
|
||||
|
||||
if ([BUILD_TYPES.ARCHIVE, BUILD_TYPES.CLIENT].includes(buildType!)) {
|
||||
@@ -41,7 +41,7 @@ export default async function iosBuilder(job: IJob): Promise<IJobResult> {
|
||||
const artifactUrl = await uploadBuildToS3(ctx);
|
||||
return { artifactUrl };
|
||||
} catch (err) {
|
||||
logErrorOnce(err);
|
||||
logErrorOnce(err, ctx.logger);
|
||||
throw err;
|
||||
} finally {
|
||||
await cleanup(ctx);
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
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' });
|
||||
l.info('running simulator build');
|
||||
ctx.logger = ctx.logger.child({ buildPhase: 'running simulator builder' });
|
||||
ctx.logger.info('running simulator build');
|
||||
await runShellAppBuilder(ctx, job);
|
||||
l.info('build complete');
|
||||
ctx.logger.info('build complete');
|
||||
}
|
||||
@@ -7,12 +7,11 @@ 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): Promise<IAndroidCredentials> {
|
||||
async function getOrCreateCredentials(jobData: IJob, logger: any): Promise<IAndroidCredentials> {
|
||||
if (!jobData.credentials) {
|
||||
const l = logger.withFields({ buildPhase: 'generating keystore' });
|
||||
const l = logger.child({ buildPhase: 'generating keystore' });
|
||||
const credentials: any = {};
|
||||
l.info('Creating keystore');
|
||||
const keystoreFilename = `/tmp/keystore-${jobData.id}.tmp.jks`;
|
||||
@@ -44,7 +43,7 @@ async function getOrCreateCredentials(jobData: IJob): Promise<IAndroidCredential
|
||||
} else {
|
||||
await sqs.sendMessage(jobData.id, UPDATE_CREDENTIALS, {
|
||||
...credentials,
|
||||
});
|
||||
}, l);
|
||||
l.info('Keystore sent to storage successfully');
|
||||
}
|
||||
await fs.unlink(keystoreFilename);
|
||||
|
||||
@@ -2,7 +2,6 @@ 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;
|
||||
@@ -18,7 +17,7 @@ export function getExperienceUrl(job: IJob) {
|
||||
|
||||
const alreadyLoggedError = Symbol('alreadyLoggedError');
|
||||
|
||||
export function logErrorOnce(err: any) {
|
||||
export function logErrorOnce(err: any, logger: any) {
|
||||
if (!err[alreadyLoggedError]) {
|
||||
logger.error(err.stack);
|
||||
err[alreadyLoggedError] = true;
|
||||
|
||||
@@ -9,13 +9,12 @@ 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) {
|
||||
async function prepareAdHocBuildCredentials(job: IJob, logger: any) {
|
||||
if (process.platform !== 'darwin') {
|
||||
throw new Error('This function should be called only on macOS!');
|
||||
}
|
||||
@@ -59,7 +58,7 @@ async function prepareAdHocBuildCredentials(job: IJob) {
|
||||
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) {
|
||||
|
||||
@@ -2,14 +2,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';
|
||||
|
||||
interface IKeychain {
|
||||
path: string;
|
||||
}
|
||||
|
||||
export async function create(ctx: IContext): Promise<IKeychain> {
|
||||
const l = logger.withFields({ buildPhase: 'creating keychain' });
|
||||
const l = ctx.logger.child({ buildPhase: 'creating keychain' });
|
||||
try {
|
||||
l.info('creating keychain...');
|
||||
const keychainInfo = await IosKeychain.createKeychain(ctx.appUUID, false);
|
||||
@@ -22,7 +21,7 @@ export async function create(ctx: IContext): Promise<IKeychain> {
|
||||
}
|
||||
|
||||
export async function remove(ctx: IContext, keychainPath: string) {
|
||||
const l = logger.withFields({ buildPhase: 'deleting keychain' });
|
||||
const l = ctx.logger.child({ buildPhase: 'deleting keychain' });
|
||||
try {
|
||||
l.info('delete keychain...');
|
||||
const keychainInfo = await IosKeychain.deleteKeychain({
|
||||
@@ -49,7 +48,7 @@ export async function importCert(
|
||||
certPassword,
|
||||
}: { keychainPath: string; certP12: string; certPassword: string },
|
||||
) {
|
||||
const l = logger.withFields({ buildPhase: 'importing certificate into keychain' });
|
||||
const l = ctx.logger.child({ buildPhase: 'importing certificate into keychain' });
|
||||
try {
|
||||
l.info('importing distribution certificate into keychain...');
|
||||
const { tempCertPath: certPath } = ctx;
|
||||
|
||||
@@ -10,7 +10,6 @@ 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);
|
||||
|
||||
@@ -21,7 +20,7 @@ export default async function runShellAppBuilder(ctx: IContext, job: IJob): Prom
|
||||
|
||||
await copyAsync(ctx.applicationFilesSrc, ctx.baseArchiveDir);
|
||||
|
||||
logger.info(
|
||||
ctx.logger.info(
|
||||
{ buildPhase: 'icons setup' },
|
||||
'ImageUtils: setting image functions to alternative sharp implementations',
|
||||
);
|
||||
@@ -49,6 +48,6 @@ export default async function runShellAppBuilder(ctx: IContext, job: IJob): Prom
|
||||
});
|
||||
}
|
||||
|
||||
logger.info({ buildPhase: 'configuring NSBundle' }, 'configuring NSBundle...');
|
||||
ctx.logger.info({ buildPhase: 'configuring NSBundle' }, 'configuring NSBundle...');
|
||||
return await IosShellApp.configureAndCopyArchiveAsync(shellAppParams);
|
||||
}
|
||||
@@ -2,24 +2,24 @@ import * as fs from 'fs-extra';
|
||||
|
||||
import { uploadFile } from 'turtle/aws/s3';
|
||||
import config from 'turtle/config';
|
||||
import logger from 'turtle/logger';
|
||||
|
||||
interface IUploadCtx {
|
||||
fakeUploadBuildPath?: string;
|
||||
s3FileKey?: string;
|
||||
uploadPath: string;
|
||||
logger: any;
|
||||
}
|
||||
|
||||
export async function uploadBuildToS3(ctx: IUploadCtx) {
|
||||
if (config.builder.fakeUpload) {
|
||||
const l = logger.withFields({ buildPhase: 'copying build artifact' });
|
||||
const l = ctx.logger.child({ buildPhase: 'copying build artifact' });
|
||||
const { fakeUploadBuildPath, uploadPath } = ctx;
|
||||
l.info('copying build to fake upload directory');
|
||||
await fs.copy(uploadPath, fakeUploadBuildPath as string);
|
||||
l.info(`copied build to ${fakeUploadBuildPath}`);
|
||||
return fakeUploadBuildPath as string;
|
||||
} else {
|
||||
const l = logger.withFields({ buildPhase: 'uploading to S3' });
|
||||
const l = ctx.logger.child({ buildPhase: 'uploading to S3' });
|
||||
l.info('uploading build artifact to S3');
|
||||
const { Location: fileLocation } = await uploadFile({
|
||||
key: ctx.s3FileKey as string,
|
||||
|
||||
+30
-30
@@ -6,7 +6,6 @@ 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';
|
||||
@@ -14,35 +13,35 @@ import { getPriorities } from 'turtle/utils/priorities';
|
||||
import * as redis from 'turtle/utils/redis';
|
||||
import { sanitizeJob } from 'turtle/validator';
|
||||
|
||||
function _maybeExit() {
|
||||
function _maybeExit(logger: any) {
|
||||
if (checkShouldExit()) {
|
||||
logger.warn('Exiting due to previously received termination signal.');
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
export async function doJob() {
|
||||
const jobData = await getJob();
|
||||
await processJob(jobData);
|
||||
_maybeExit();
|
||||
export async function doJob(logger: any) {
|
||||
const jobData = await getJob(logger);
|
||||
await processJob(jobData, logger);
|
||||
_maybeExit(logger);
|
||||
}
|
||||
|
||||
export async function getJob() {
|
||||
_maybeExit();
|
||||
export async function getJob(logger: any) {
|
||||
_maybeExit(logger);
|
||||
logger.info('Fetching job');
|
||||
while (true) {
|
||||
try {
|
||||
let job = null;
|
||||
const priorities = await getPriorities();
|
||||
const priorities = await getPriorities(logger);
|
||||
for (const priority of priorities) {
|
||||
job = await sqs.receiveMessage(priority);
|
||||
_maybeExit();
|
||||
job = await sqs.receiveMessage(priority, logger);
|
||||
_maybeExit(logger);
|
||||
if (job != null) {
|
||||
job.priority = priority;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_maybeExit();
|
||||
_maybeExit(logger);
|
||||
if (job !== null) {
|
||||
return job;
|
||||
}
|
||||
@@ -52,30 +51,30 @@ export async function getJob() {
|
||||
}
|
||||
}
|
||||
|
||||
async function processJob(jobData: any) {
|
||||
async function processJob(jobData: any, logger: any) {
|
||||
const receiptHandle = jobData.ReceiptHandle;
|
||||
const { priority } = jobData;
|
||||
let timeoutId;
|
||||
try {
|
||||
const rawJob = JSON.parse(jobData.Body);
|
||||
timeoutId = failAfterMaxJobTime(priority, receiptHandle, rawJob);
|
||||
timeoutId = failAfterMaxJobTime(priority, receiptHandle, rawJob, logger);
|
||||
const job = await sanitizeJob(rawJob);
|
||||
|
||||
setCurrentJobId(job.id);
|
||||
const pingerHandle = sqs.changeMessageVisibilityRecurring(priority, jobData.ReceiptHandle, job.id);
|
||||
const pingerHandle = sqs.changeMessageVisibilityRecurring(priority, jobData.ReceiptHandle, job.id, logger);
|
||||
|
||||
logger.info(`Doing job MessageId=${jobData.MessageId} BuildId=${job.id} ${Date.now()}`);
|
||||
|
||||
const cancelled = await redis.checkIfCancelled(job.id);
|
||||
const cancelled = await redis.checkIfCancelled(job.id, logger);
|
||||
if (cancelled) {
|
||||
logger.info('The job has been cancelled');
|
||||
} else {
|
||||
redis.registerListener(job.id, () => deleteMessage(priority, receiptHandle));
|
||||
redis.registerListener(job.id, () => deleteMessage(priority, receiptHandle, logger), logger);
|
||||
const startTimestamp = Date.now();
|
||||
let buildFailed = false;
|
||||
const buildType = _.get(job, 'config.buildType', 'default');
|
||||
try {
|
||||
const status = await build(job);
|
||||
const status = await build(job, logger);
|
||||
buildStatusMetric.add(buildType, status);
|
||||
buildFailed = !status;
|
||||
} catch (err) {
|
||||
@@ -93,13 +92,13 @@ async function processJob(jobData: any) {
|
||||
);
|
||||
buildDurationMetric.addTotalDuration(buildType, totalBuildDurationSecs, !buildFailed);
|
||||
}
|
||||
redis.unregisterListeners();
|
||||
redis.unregisterListeners(logger);
|
||||
}
|
||||
logger.info(`Job done MessageId=${jobData.MessageId} BuildId=${job.id} ${Date.now()}`);
|
||||
}
|
||||
clearInterval(pingerHandle);
|
||||
} finally {
|
||||
await deleteMessage(priority, receiptHandle);
|
||||
await deleteMessage(priority, receiptHandle, logger);
|
||||
if (timeoutId) {
|
||||
logger.info('Clearing job failer timeout...');
|
||||
clearTimeout(timeoutId);
|
||||
@@ -107,11 +106,11 @@ async function processJob(jobData: any) {
|
||||
}
|
||||
}
|
||||
|
||||
function failAfterMaxJobTime(priority: string, receiptHandle: string, job: any) {
|
||||
function failAfterMaxJobTime(priority: string, receiptHandle: string, job: any, logger: any) {
|
||||
return setTimeout(async () => {
|
||||
try {
|
||||
sqs.sendMessage(job.id, BUILD.JOB_STATES.ERRORED, { turtleVersion });
|
||||
await deleteMessage(priority, receiptHandle);
|
||||
sqs.sendMessage(job.id, BUILD.JOB_STATES.ERRORED, { turtleVersion }, logger);
|
||||
await deleteMessage(priority, receiptHandle, logger);
|
||||
} finally {
|
||||
logger.error('Going to terminate turtle agent, just in case...');
|
||||
process.exit(1);
|
||||
@@ -119,26 +118,27 @@ function failAfterMaxJobTime(priority: string, receiptHandle: string, job: any)
|
||||
}, config.builder.maxJobTimeMs);
|
||||
}
|
||||
|
||||
async function build(job: any) {
|
||||
async function build(job: any, logger: 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',
|
||||
});
|
||||
const result = await (builders as any)[job.platform](job);
|
||||
}, buildLogger);
|
||||
const result = await builders[job.platform](job, buildLogger);
|
||||
const buildDuration = calculateBuildDuration();
|
||||
sqs.sendMessage(job.id, BUILD.JOB_STATES.FINISHED, {
|
||||
...result,
|
||||
turtleVersion,
|
||||
buildDuration,
|
||||
});
|
||||
}, buildLogger);
|
||||
return true;
|
||||
} catch (err) {
|
||||
logErrorOnce(err);
|
||||
logErrorOnce(err, buildLogger);
|
||||
const buildDuration = calculateBuildDuration();
|
||||
let reason;
|
||||
if (err instanceof BuildError) {
|
||||
@@ -148,14 +148,14 @@ async function build(job: any) {
|
||||
turtleVersion,
|
||||
buildDuration,
|
||||
...reason && { reason },
|
||||
});
|
||||
}, buildLogger);
|
||||
return false;
|
||||
} finally {
|
||||
await logger.cleanup(job);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteMessage(priority: string, receiptHandle: string) {
|
||||
async function deleteMessage(priority: string, receiptHandle: string, logger: any) {
|
||||
try {
|
||||
setCurrentJobId(null);
|
||||
await sqs.deleteMessage(priority, receiptHandle);
|
||||
|
||||
@@ -4,7 +4,6 @@ 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';
|
||||
@@ -70,8 +69,6 @@ const logger = bunyan.createLogger({
|
||||
streams,
|
||||
});
|
||||
|
||||
logger.withFields = (extraFields: any) => withFields(logger, extraFields);
|
||||
|
||||
logger.init = async (job: IJob) => {
|
||||
return await s3logger.init(job);
|
||||
};
|
||||
@@ -83,10 +80,3 @@ 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);
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
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() {
|
||||
export function register(logger: any) {
|
||||
for (const metricName of [TURTLE_BUILD_DURATION_METRIC_NAME, TOTAL_BUILD_DURATION_METRIC_NAME]) {
|
||||
logger.info(`Registered AWS Cloudwatch metric: ${metricName}`);
|
||||
cloudwatch.registerMetric({
|
||||
|
||||
@@ -2,11 +2,10 @@ 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() {
|
||||
export function register(logger: any) {
|
||||
logger.info(`Registered AWS Cloudwatch metric: ${BUILD_METRIC_NAME}`);
|
||||
cloudwatch.registerMetric({
|
||||
metricName: BUILD_METRIC_NAME,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
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() {
|
||||
export async function init(logger: any) {
|
||||
logger.info('Initializing Cloudwatch metrics');
|
||||
await cloudwatch.init();
|
||||
metricsToRegister.forEach((metric) => metric.register());
|
||||
await cloudwatch.init(logger);
|
||||
metricsToRegister.forEach((metric) => metric.register(logger));
|
||||
}
|
||||
+3
-3
@@ -31,11 +31,11 @@ async function main() {
|
||||
);
|
||||
LoggerDetach.configure(logger);
|
||||
if (setup[config.platform]) {
|
||||
await setup[config.platform]();
|
||||
await setup[config.platform](logger);
|
||||
}
|
||||
|
||||
try {
|
||||
const redis = await getRedisClient(RedisClient.Configuration);
|
||||
const redis = await getRedisClient(logger, 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();
|
||||
await doJob(logger);
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Failed to do a job');
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import logger from 'turtle/logger';
|
||||
import commonSetup from 'turtle/setup/common';
|
||||
|
||||
export default async function setup() {
|
||||
export default async function setup(logger: any) {
|
||||
logger.info('Setting up environment...');
|
||||
await commonSetup();
|
||||
await commonSetup(logger);
|
||||
logger.info('Finished setting up environment');
|
||||
}
|
||||
+2
-2
@@ -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() {
|
||||
await metrics.init();
|
||||
export default async function commonSetup(logger: any) {
|
||||
await metrics.init(logger);
|
||||
await fs.remove(config.directories.temporaryFilesRoot);
|
||||
await fs.remove(config.directories.tempS3LogsDir);
|
||||
}
|
||||
+2
-3
@@ -3,7 +3,6 @@ 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() {
|
||||
@@ -17,10 +16,10 @@ async function deleteProvisioningProfilesFromHomedir() {
|
||||
}
|
||||
}
|
||||
|
||||
export default async function setup() {
|
||||
export default async function setup(logger: any) {
|
||||
logger.info('Setting up environment...');
|
||||
await deleteProvisioningProfilesFromHomedir();
|
||||
await keychain.cleanUp();
|
||||
await commonSetup();
|
||||
await commonSetup(logger);
|
||||
logger.info('Finished setting up environment');
|
||||
}
|
||||
@@ -2,7 +2,6 @@ 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';
|
||||
@@ -40,7 +39,7 @@ export function createDefaultConfigurationKey(platform = config.platform) {
|
||||
return `${configPrefix(platform)}:default`;
|
||||
}
|
||||
|
||||
export async function getPriorities() {
|
||||
export async function getPriorities(logger: any) {
|
||||
if (config.env === 'test') {
|
||||
return NORMAL_CONFIGURATION;
|
||||
}
|
||||
|
||||
+6
-7
@@ -1,7 +1,6 @@
|
||||
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;
|
||||
@@ -47,7 +46,7 @@ const redisClients: IRedisClients = {
|
||||
[RedisClient.Configuration]: null,
|
||||
};
|
||||
|
||||
export async function getRedisClient(type = RedisClient.Default) {
|
||||
export async function getRedisClient(logger: any, type = RedisClient.Default) {
|
||||
if (!redisClients[type]) {
|
||||
try {
|
||||
redisClients[type] = await connect(MILLIS_CONNECTION_TIMEOUT, type);
|
||||
@@ -58,9 +57,9 @@ export async function getRedisClient(type = RedisClient.Default) {
|
||||
return redisClients[type];
|
||||
}
|
||||
|
||||
export async function checkIfCancelled(jobId: string) {
|
||||
export async function checkIfCancelled(jobId: string, logger: any) {
|
||||
try {
|
||||
const redis = await getRedisClient();
|
||||
const redis = await getRedisClient(logger);
|
||||
return await redis.get(`jobs:cancelled:${jobId}`);
|
||||
} catch (err) {
|
||||
if (config.deploymentEnv === 'development') {
|
||||
@@ -71,7 +70,7 @@ export async function checkIfCancelled(jobId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function registerListener(jobId: string, deleteMessage: any) {
|
||||
export async function registerListener(jobId: string, deleteMessage: any, logger: any) {
|
||||
try {
|
||||
const redis = await getRedisClient(RedisClient.Subscriber);
|
||||
redis.subscribe('jobs:cancelled');
|
||||
@@ -88,9 +87,9 @@ export async function registerListener(jobId: string, deleteMessage: any) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function unregisterListeners() {
|
||||
export async function unregisterListeners(logger: any) {
|
||||
try {
|
||||
const redis = await getRedisClient();
|
||||
const redis = await getRedisClient(logger);
|
||||
redis.removeAllListeners('message');
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
|
||||
@@ -346,12 +346,13 @@
|
||||
pify "^4.0.0"
|
||||
retry-request "^4.0.0"
|
||||
|
||||
"@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==
|
||||
"@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==
|
||||
dependencies:
|
||||
"@google-cloud/logging" "^4.1.0"
|
||||
google-auth-library "^3.1.0"
|
||||
|
||||
"@google-cloud/logging@^4.1.0":
|
||||
version "4.4.0"
|
||||
@@ -4688,8 +4689,6 @@ 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"
|
||||
@@ -5310,6 +5309,14 @@ 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"
|
||||
@@ -5682,6 +5689,21 @@ 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"
|
||||
@@ -11473,8 +11495,6 @@ 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"
|
||||
|
||||
Reference in new issue
Block a user