Improve handling of parallel CI jobs. (#1968)

1. Sometimes, it is necessary to run multiple parallel CI jobs on the same
physical machine w/o any virtualization or containerization (a good
example of that is building an iOS app). To work properly in that
environment, realm should download a temp file to it's own unique temp
folder to avoid clashes (that are actually fail with `symlink` syscall
failing with `EEXISTS`).

2. To avoid an ever-growing temp folder, a new environment variable was
introduced, that you can set to be inside a job's folder. That way, CI
will be responsible for cleaning up after itself and keeping only the
necessary amount of files.

Theoretically, all of that can be solved by overriding `TMP` env
variable but in practice in many-many environments it breaks the other
part of toolchain (even Xcode builds themselves sometimes).
This commit is contained in:
Igor Mandrigin 2018-08-17 10:27:09 +02:00 committed by Kenneth Geisshirt
parent a58a984f3f
commit d0ba40c33a
2 changed files with 33 additions and 3 deletions

View File

@ -19,6 +19,7 @@
* React Native for Android now supports the Android Gradle Plugin 3.0 (#1742).
* [Sync] Classes used by the Object-level permission system are now automatically part of the schema for Query-based Realms (#1966).
* [Sync] Fixed distinct queries with query-based sync (broken since v2.11.0).
* Support parallel run of muliple iOS builds with React Native on the same CI machine.
### Internals
* Updated to Object Store commit: 97fd03819f398b3c81c8b007feaca8636629050b

View File

@ -25,9 +25,36 @@ const child_process = require('child_process');
const fetch = require('node-fetch');
const ini = require('ini');
const decompress = require('decompress');
const crypto = require('crypto');
const LOCKFILE_NAME = 'download-realm.lock';
const TEMP_DIR_SUFFIX = generateRandomString();
function generateRandomString() {
return crypto.randomBytes(20).toString('hex');
}
function ensureDirExists(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
}
function getTempDir() {
/*
* Each run of this script will have it's own temp directory.
* That way, parallel runs of this script on a same computer won't
* conflict.
*
* It is also possible to overrride this temp directory by setting
* REALM_DOWNLOAD_CORE_TEMP_DIR so, for instance, CI systems will
* be able to clean up files.
*/
return process.env.REALM_DOWNLOAD_CORE_TEMP_DIR ||
path.join(os.tmpdir(), TEMP_DIR_SUFFIX);
}
function exec() {
const args = Array.from(arguments);
return new Promise((resolve, reject) => {
@ -102,7 +129,7 @@ function extract(downloadedArchive, targetFolder, archiveRootFolder) {
if (!archiveRootFolder) {
return decompress(downloadedArchive, targetFolder);
} else {
const tempExtractLocation = path.resolve(os.tmpdir(), path.basename(downloadedArchive, path.extname(downloadedArchive)));
const tempExtractLocation = path.resolve(getTempDir(), path.basename(downloadedArchive, path.extname(downloadedArchive)));
return decompress(downloadedArchive, tempExtractLocation)
.then(() => fs.readdir(path.resolve(tempExtractLocation, archiveRootFolder)))
.then(items => Promise.all(items.map(item => {
@ -115,8 +142,8 @@ function extract(downloadedArchive, targetFolder, archiveRootFolder) {
}
function acquire(desired, target) {
const corePath = desired.CORE_ARCHIVE && path.resolve(os.tmpdir(), desired.CORE_ARCHIVE);
const syncPath = desired.SYNC_ARCHIVE && path.resolve(os.tmpdir(), desired.SYNC_ARCHIVE);
const corePath = desired.CORE_ARCHIVE && path.resolve(getTempDir(), desired.CORE_ARCHIVE);
const syncPath = desired.SYNC_ARCHIVE && path.resolve(getTempDir(), desired.SYNC_ARCHIVE);
return fs.emptyDir(target)
.then(() => corePath && download(desired.CORE_SERVER_FOLDER, desired.CORE_ARCHIVE, corePath))
@ -261,6 +288,8 @@ if (options.debug) {
realmDir += '-dbg'
}
ensureDirExists(getTempDir());
const dependencies = ini.parse(fs.readFileSync(path.resolve(__dirname, '../dependencies.list'), 'utf8'));
(options.sync ? getSyncRequirements : getCoreRequirements)(dependencies, options)