mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-01 18:19:43 +00:00
feat(release): add mobile build preflight
This commit is contained in:
1 parent
8bf93f372b
commit
5947220fc6
8 files changed
+411
-21
No files matched your search
@@ -0,0 +1,97 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { chmodSync, cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { validateFreeDisk, validateLocalEas, validateReleaseSources } from './release-preflight.mjs';
|
||||
|
||||
const repoRoot = join(import.meta.dirname, '..', '..', '..');
|
||||
|
||||
function fixture() {
|
||||
const root = mkdtempSync(join(tmpdir(), 'sauci-release-preflight-'));
|
||||
cpSync(join(repoRoot, 'apps', 'mobile'), join(root, 'apps', 'mobile'), { recursive: true });
|
||||
cpSync(join(repoRoot, '.gitignore'), join(root, '.gitignore'));
|
||||
return root;
|
||||
}
|
||||
|
||||
function replace(root, relative, from, to) {
|
||||
const file = join(root, relative);
|
||||
writeFileSync(file, readFileSync(file, 'utf8').replace(from, to));
|
||||
}
|
||||
|
||||
function validate(root) {
|
||||
return validateReleaseSources({ root, isTracked: () => true });
|
||||
}
|
||||
|
||||
test('accepts current checked-in mobile release sources', () => {
|
||||
const root = fixture();
|
||||
try {
|
||||
const result = validate(root);
|
||||
assert.match(result.publicVersion, /^\d+\.\d+\.\d+$/);
|
||||
assert.match(result.iosBuild, /^\d+$/);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects a widget Xcode build setting that overrides its plist', () => {
|
||||
const root = fixture();
|
||||
try {
|
||||
const { iosBuild } = validate(root);
|
||||
const project = 'apps/mobile/ios/Sauci.xcodeproj/project.pbxproj';
|
||||
const source = readFileSync(join(root, project), 'utf8');
|
||||
const expected = `CURRENT_PROJECT_VERSION = ${iosBuild};`;
|
||||
const replacement = `CURRENT_PROJECT_VERSION = ${Number(iosBuild) + 1};`;
|
||||
assert.ok(source.includes(expected));
|
||||
replace(root, project, expected, replacement);
|
||||
assert.ok(readFileSync(join(root, project), 'utf8').includes(replacement));
|
||||
assert.throws(() => validate(root), /iOS app, widget, and effective widget Xcode build numbers disagree/);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects an OTA certificate without its root archive allowlist', () => {
|
||||
const root = fixture();
|
||||
try {
|
||||
const archiveRules = readFileSync(join(root, '.gitignore'), 'utf8');
|
||||
assert.ok(archiveRules.includes('!apps/mobile/certs/certificate.pem'));
|
||||
replace(root, '.gitignore', '!apps/mobile/certs/certificate.pem', '');
|
||||
assert.ok(!readFileSync(join(root, '.gitignore'), 'utf8').includes('!apps/mobile/certs/certificate.pem'));
|
||||
assert.throws(
|
||||
() => validate(root),
|
||||
/must later allowlist !apps\/mobile\/certs\/certificate\.pem/,
|
||||
);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('requires the local EAS plugin executable rather than its package directory', () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'sauci-eas-plugin-'));
|
||||
const executable = join(root, 'bin-run');
|
||||
try {
|
||||
writeFileSync(executable, '#!/bin/sh\n');
|
||||
chmodSync(executable, 0o755);
|
||||
assert.throws(
|
||||
() => validateLocalEas({ EAS_LOCAL_BUILD_PLUGIN_PATH: root }),
|
||||
/executable bin\/run, not a package directory/,
|
||||
);
|
||||
const bin = join(root, 'bin');
|
||||
mkdirSync(bin);
|
||||
const run = join(bin, 'run');
|
||||
cpSync(executable, run);
|
||||
chmodSync(run, 0o755);
|
||||
validateLocalEas({ EAS_LOCAL_BUILD_PLUGIN_PATH: run });
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('requires enough disk before an expensive local build', () => {
|
||||
assert.throws(
|
||||
() => validateFreeDisk('/fixture', () => ({ bavail: 19, bsize: 1024 ** 3 })),
|
||||
/need at least 20 GiB free/,
|
||||
);
|
||||
validateFreeDisk('/fixture', () => ({ bavail: 20, bsize: 1024 ** 3 }));
|
||||
});
|
||||
Reference in new issue
Block a user