- jenkins-thin-artifact-ci 마일스톤 계획 수립 - macos certified build 스크립트 및 테스트 업데이트 - docs/macos-certified-build.md 문서 갱신 - ci secrets setup 스크립트 추가
131 lines
4.3 KiB
Dart
131 lines
4.3 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
group('certified macOS build scripts', () {
|
|
test('secret setup stores repeatable encrypted CI inputs', () {
|
|
final script = _readScript('scripts/setup-appsok-ci-secrets.sh');
|
|
|
|
expect(script, contains(r'age-keygen -o "$AGE_KEY"'));
|
|
expect(
|
|
script,
|
|
contains(r'SOPS_PATH_REGEX="$(SECRET_FILE="$SECRET_FILE"'),
|
|
);
|
|
expect(
|
|
script,
|
|
contains(r'print(re.escape(os.environ["SECRET_FILE"]) + "$")'),
|
|
);
|
|
expect(script, contains(r'path_regex: $SOPS_PATH_REGEX'));
|
|
expect(
|
|
script,
|
|
contains(r'sops --filename-override "$SECRET_FILE" -e "$tmp"'),
|
|
);
|
|
expect(script, contains(r'SOPS_AGE_KEY_FILE="$AGE_KEY" sops -d'));
|
|
expect(script, contains('security unlock-keychain'));
|
|
expect(script, contains('security set-key-partition-list'));
|
|
expect(script, contains('xcrun notarytool store-credentials'));
|
|
expect(script, contains('xcrun notarytool history --keychain-profile'));
|
|
});
|
|
|
|
test('certified build keeps Flutter gates before release packaging', () {
|
|
final script = _readScript('scripts/build-certified-macos.sh');
|
|
|
|
_expectOrder(script, 'flutter pub get', 'flutter analyze');
|
|
_expectOrder(script, 'flutter analyze', 'flutter test');
|
|
_expectOrder(script, 'flutter test', 'flutter build macos');
|
|
_expectOrder(script, 'flutter build macos', 'codesign --force');
|
|
});
|
|
|
|
test('certified build signs bundled adb before app notarization', () {
|
|
final script = _readScript('scripts/build-certified-macos.sh');
|
|
|
|
expect(
|
|
script,
|
|
contains(r'ADB_PATH="$APP_PATH/Contents/Resources/adb-runtime/adb"'),
|
|
);
|
|
expect(
|
|
script,
|
|
contains(
|
|
r'codesign --force --options runtime --timestamp --sign "$IDENTITY" "$ADB_PATH"',
|
|
),
|
|
);
|
|
expect(
|
|
script,
|
|
contains(r'codesign --verify --strict --verbose=2 "$ADB_PATH"'),
|
|
);
|
|
_expectOrder(
|
|
script,
|
|
r'codesign --force --options runtime --timestamp --sign "$IDENTITY" "$ADB_PATH"',
|
|
'--entitlements macos/Runner/Release.entitlements',
|
|
);
|
|
});
|
|
|
|
test('certified build notarizes, staples, assesses, and zips artifact', () {
|
|
final script = _readScript('scripts/build-certified-macos.sh');
|
|
|
|
expect(
|
|
script,
|
|
contains(
|
|
r'xcrun notarytool submit "$NOTARY_ZIP" --keychain-profile "$NOTARY_PROFILE" --wait',
|
|
),
|
|
);
|
|
expect(script, contains(r'xcrun stapler staple -v "$APP_PATH"'));
|
|
expect(
|
|
script,
|
|
contains(r'spctl --assess --type execute --verbose=4 "$APP_PATH"'),
|
|
);
|
|
expect(
|
|
script,
|
|
contains(
|
|
'FINAL_ZIP="build/macos/Build/Products/Release/AppSok-certified.zip"',
|
|
),
|
|
);
|
|
expect(script, contains(r'FINAL_SHA256="$FINAL_ZIP.sha256"'));
|
|
expect(
|
|
script,
|
|
contains(r'shasum -a 256 "$FINAL_ZIP" | tee "$FINAL_SHA256"'),
|
|
);
|
|
expect(script, contains(r'ls -lh "$FINAL_SHA256"'));
|
|
|
|
_expectOrder(
|
|
script,
|
|
r'xcrun notarytool submit "$NOTARY_ZIP" --keychain-profile "$NOTARY_PROFILE" --wait',
|
|
r'xcrun stapler staple -v "$APP_PATH"',
|
|
);
|
|
_expectOrder(
|
|
script,
|
|
r'xcrun stapler staple -v "$APP_PATH"',
|
|
r'spctl --assess --type execute --verbose=4 "$APP_PATH"',
|
|
);
|
|
_expectOrder(
|
|
script,
|
|
r'spctl --assess --type execute --verbose=4 "$APP_PATH"',
|
|
r'ditto -c -k --keepParent "$APP_PATH" "$FINAL_ZIP"',
|
|
);
|
|
_expectOrder(
|
|
script,
|
|
r'ditto -c -k --keepParent "$APP_PATH" "$FINAL_ZIP"',
|
|
r'shasum -a 256 "$FINAL_ZIP" | tee "$FINAL_SHA256"',
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
String _readScript(String path) => File(path).readAsStringSync();
|
|
|
|
void _expectOrder(String source, String before, String after) {
|
|
final beforeIndex = _requiredIndex(source, before);
|
|
final afterIndex = _requiredIndex(source, after);
|
|
expect(
|
|
beforeIndex,
|
|
lessThan(afterIndex),
|
|
reason: '"$before" must appear before "$after"',
|
|
);
|
|
}
|
|
|
|
int _requiredIndex(String source, String needle) {
|
|
final index = source.indexOf(needle);
|
|
expect(index, greaterThanOrEqualTo(0), reason: 'missing "$needle"');
|
|
return index;
|
|
}
|