공개된 소스
소스 ZIP 다운로드
CH-J Tools Mobile
특정 애플리케이션 릴리스의 디렉터리와 파일을 살펴보세요.
CH-J / CH-J Tools Mobile
175 개 파일
sm-mobile-20261016T185235Z-8e93ecc1b59a
추가됨: · 빌드됨:
1
#!/usr/bin/env python32
"""Package a built mobile release and allowlisted source without signing secrets.4
Run tool/build.py distribution first. This script does not build or deploy.5
"""6
import datetime7
import hashlib8
import json9
from pathlib import Path10
import re11
import shutil12
import subprocess13
import tempfile14
import zipfile16
ROOT = Path(__file__).resolve().parents[1]17
WORKSPACE = ROOT.parent18
INFO = (ROOT / 'lib/core/build_info.dart').read_text()19
BUILD_ID = re.search(r"const buildId = '([^']+)'", INFO).group(1)20
VERSION = re.search(r"const appVersion = '([^']+)'", INFO).group(1)21
if not re.fullmatch(r'sm-mobile-\d{8}T\d{6}Z-[a-f0-9]{12}', BUILD_ID):22
raise SystemExit('Invalid build ID')23
RELEASE = WORKSPACE / 'POUZE-VYVOJOVA-DATA-BEZ-ZAVISLOSTI-NA-APP/builds' / BUILD_ID24
RELEASE.mkdir(exist_ok=False)25
FILES = RELEASE / 'source/files'26
FILES.mkdir(parents=True)28
# Explicit roots; local SDK configuration and generated Xcode configuration are excluded.29
selected = [ROOT / name for name in [30
'README.md', 'LICENSE', 'NOTICE', 'pubspec.yaml', 'pubspec.lock',31
'analysis_options.yaml', 'l10n.yaml', '.gitignore', '.metadata',32
'android/build.gradle.kts', 'android/settings.gradle.kts', 'android/gradle.properties',33
'android/gradlew', 'android/gradlew.bat', 'android/app/build.gradle.kts',34
'ios/Flutter/AppFrameworkInfo.plist', 'ios/Flutter/Debug.xcconfig', 'ios/Flutter/Release.xcconfig',35
'ios/Runner.xcodeproj/project.pbxproj', 'ios/Runner.xcworkspace/contents.xcworkspacedata',36
]]37
for folder in ['lib', 'assets', 'docs', 'test', 'integration_test', 'tool',38
'android/gradle/wrapper', 'android/app/src', 'ios/Runner', 'ios/RunnerTests',39
'ios/Runner.xcodeproj/xcshareddata', 'ios/Runner.xcworkspace/xcshareddata']:40
selected.extend((ROOT / folder).rglob('*'))41
entries = []42
for source in sorted(set(selected)):43
if source.is_symlink():44
raise SystemExit('Symlink in source allowlist: ' + str(source))45
if not source.is_file():46
continue47
relative = source.relative_to(ROOT)48
if source.name.startswith('.') and source.name not in ['.gitignore', '.metadata']:49
continue50
if any(part in ['__pycache__', '.dart_tool', 'build', 'ephemeral', '.swiftpm', 'xcuserdata'] for part in relative.parts):51
continue52
if source.name in ['GeneratedPluginRegistrant.java', 'GeneratedPluginRegistrant.h', 'GeneratedPluginRegistrant.m']:53
continue54
if source.suffix.lower() in ['.jks', '.keystore', '.mobileprovision', '.p12', '.p8', '.pem', '.key', '.pyc'] or source.name in ['key.properties', 'local.properties', 'Generated.xcconfig', 'flutter_export_environment.sh']:55
raise SystemExit('Private or machine-specific file in source allowlist: ' + str(relative))56
if not re.fullmatch(r'[A-Za-z0-9_@.\/-]+', relative.as_posix()):57
raise SystemExit('Unsupported source browser path: ' + str(relative))58
data = source.read_bytes()59
destination = FILES / relative60
destination.parent.mkdir(parents=True, exist_ok=True)61
destination.write_bytes(data)62
destination.chmod(0o755 if source.name == 'gradlew' else 0o644)63
try:64
text = data.decode('utf-8')65
is_text = '\0' not in text66
except UnicodeDecodeError:67
text, is_text = '', False68
entries.append(dict(path=relative.as_posix(), size=len(data), sha256=hashlib.sha256(data).hexdigest(),69
text=is_text, lines=len(text.splitlines()) if is_text else None))70
archive = RELEASE / f'CH-J-SM-Mobile-{VERSION}-{BUILD_ID}-source.zip'71
with zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as handle:72
for entry in entries:73
handle.write(FILES / entry['path'], 'app/' + entry['path'])74
built_at = datetime.datetime.strptime(BUILD_ID.split('-')[2], '%Y%m%dT%H%M%SZ').replace(tzinfo=datetime.timezone.utc).isoformat()75
manifest = dict(buildId=BUILD_ID, version=VERSION, edition='mobile', builtAt=built_at,76
addedAt=datetime.datetime.now(datetime.timezone.utc).isoformat(),77
license='CH-J Proprietary Software License 1.14', archive=archive.name,78
archiveSize=archive.stat().st_size, archiveSha256=hashlib.sha256(archive.read_bytes()).hexdigest(), files=entries)79
(RELEASE / 'source/manifest.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + '\n')81
artifacts = []82
def add(path, kind, platform, arch, signing, certificate=None):83
data = path.read_bytes()84
entry = dict(id=hashlib.sha256((BUILD_ID + ':' + kind).encode()).hexdigest()[:24],85
type='mobile', kind=kind, active=True, version=VERSION, build_id=BUILD_ID,86
platform=platform, arch=arch, signing=signing, channel='alpha', artifact_role='installer',87
filename=path.name, file=f'files/mobile/{BUILD_ID}/{path.name}', size=len(data),88
sha512=hashlib.sha512(data).hexdigest(), sha256=hashlib.sha256(data).hexdigest(),89
published_at=manifest['addedAt'])90
if certificate:91
entry['certificateSha256'] = certificate92
artifacts.append(entry)94
# Verify APK signature using the same Java runtime as the Flutter Android toolchain.95
import os96
java_home = '/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home'97
android_sdk = Path(dict(line.split('=', 1) for line in (ROOT / 'android/local.properties').read_text().splitlines() if '=' in line)['sdk.dir'])98
checker = sorted((android_sdk / 'build-tools').glob('*/apksigner'))[-1]99
signature = subprocess.check_output([str(checker), 'verify', '--verbose', '--print-certs', str(ROOT / 'build/app/outputs/flutter-apk/app-release.apk')], env={**os.environ, 'JAVA_HOME': java_home}, text=True)100
(RELEASE / 'android-signature.txt').write_text(signature)101
certificate = re.search(r'Signer #1 certificate SHA-256 digest: ([a-f0-9]+)', signature).group(1)102
for kind, original in [('apk', 'build/app/outputs/flutter-apk/app-release.apk'), ('aab', 'build/app/outputs/bundle/release/app-release.aab')]:103
destination = RELEASE / f'CH-J-SM-Mobile-{VERSION}-{BUILD_ID}.{kind}'104
shutil.copyfile(ROOT / original, destination)105
add(destination, kind, 'android', 'arm64-v8a / armeabi-v7a / x86_64', 'android-release', certificate)107
# ZIP the actual device .app under Payload without inventing an Apple signature.108
ipa = RELEASE / f'CH-J-SM-Mobile-{VERSION}-{BUILD_ID}-unsigned.ipa'109
with tempfile.TemporaryDirectory() as temporary:110
payload = Path(temporary) / 'Payload'111
payload.mkdir()112
subprocess.run(['ditto', str(ROOT / 'build/ios/iphoneos/Runner.app'), str(payload / 'Runner.app')], check=True)113
subprocess.run(['ditto', '-c', '-k', '--keepParent', str(payload), str(ipa)], check=True)114
add(ipa, 'ipa', 'ios', 'arm64', 'unsigned')115
simulator = ROOT / 'build/ios/iphonesimulator/Runner.app'116
if simulator.is_dir():117
destination = RELEASE / f'CH-J-SM-Mobile-{VERSION}-{BUILD_ID}-simulator.app.zip'118
subprocess.run(['ditto', '-c', '-k', '--keepParent', str(simulator), str(destination)], check=True)119
add(destination, 'simulator', 'ios-simulator', 'arm64', 'simulator-only')120
(RELEASE / 'mobile-release.json').write_text(json.dumps(dict(buildId=BUILD_ID, version=VERSION, artifacts=artifacts), indent=2) + '\n')121
(RELEASE / 'SHA512SUMS.txt').write_text(''.join(f"{entry['sha512']} {entry['filename']}\n" for entry in artifacts) + hashlib.sha512(archive.read_bytes()).hexdigest() + ' ' + archive.name + '\n')122
print(json.dumps(dict(release=str(RELEASE), buildId=BUILD_ID, files=len(entries), artifacts=len(artifacts))))SHA-256
f811f21c261a3eab38be61b3e30694a7729803bcc95b7a29d4ba35708addac1eCH-J Proprietary Software License 1.14 · 소스는 CH-J Proprietary Software License 1.14에 따라 제공됩니다. 소스를 공개한다고 해서 라이선스 조항이 바뀌거나 추가 권리가 부여되지는 않습니다.
압축 파일 SHA-256
46e427a43e87af091004f36145590bda8fe90086dfb0f631afeb5029fb0b5655