CH-J Tools MobileSSH를 통한 서버 관리
메뉴
공개된 소스

CH-J Tools Mobile

특정 애플리케이션 릴리스의 디렉터리와 파일을 살펴보세요.

소스 ZIP 다운로드
CH-J / CH-J Tools Mobile 175 개 파일
sm-mobile-20261016T185235Z-8e93ecc1b59a 추가됨: · 빌드됨:
prepare_release.py 추가됨: 날짜 기록 없음 7.3 KB · 122 줄
코드 파일 다운로드
1#!/usr/bin/env python3
2"""Package a built mobile release and allowlisted source without signing secrets.
4Run tool/build.py distribution first. This script does not build or deploy.
5"""
6import datetime
7import hashlib
8import json
9from pathlib import Path
10import re
11import shutil
12import subprocess
13import tempfile
14import zipfile
16ROOT = Path(__file__).resolve().parents[1]
17WORKSPACE = ROOT.parent
18INFO = (ROOT / 'lib/core/build_info.dart').read_text()
19BUILD_ID = re.search(r"const buildId = '([^']+)'", INFO).group(1)
20VERSION = re.search(r"const appVersion = '([^']+)'", INFO).group(1)
21if not re.fullmatch(r'sm-mobile-\d{8}T\d{6}Z-[a-f0-9]{12}', BUILD_ID):
22 raise SystemExit('Invalid build ID')
23RELEASE = WORKSPACE / 'POUZE-VYVOJOVA-DATA-BEZ-ZAVISLOSTI-NA-APP/builds' / BUILD_ID
24RELEASE.mkdir(exist_ok=False)
25FILES = RELEASE / 'source/files'
26FILES.mkdir(parents=True)
28# Explicit roots; local SDK configuration and generated Xcode configuration are excluded.
29selected = [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]]
37for 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('*'))
41entries = []
42for 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 continue
47 relative = source.relative_to(ROOT)
48 if source.name.startswith('.') and source.name not in ['.gitignore', '.metadata']:
49 continue
50 if any(part in ['__pycache__', '.dart_tool', 'build', 'ephemeral', '.swiftpm', 'xcuserdata'] for part in relative.parts):
51 continue
52 if source.name in ['GeneratedPluginRegistrant.java', 'GeneratedPluginRegistrant.h', 'GeneratedPluginRegistrant.m']:
53 continue
54 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 / relative
60 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 text
66 except UnicodeDecodeError:
67 text, is_text = '', False
68 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))
70archive = RELEASE / f'CH-J-SM-Mobile-{VERSION}-{BUILD_ID}-source.zip'
71with 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'])
74built_at = datetime.datetime.strptime(BUILD_ID.split('-')[2], '%Y%m%dT%H%M%SZ').replace(tzinfo=datetime.timezone.utc).isoformat()
75manifest = 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')
81artifacts = []
82def 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'] = certificate
92 artifacts.append(entry)
94# Verify APK signature using the same Java runtime as the Flutter Android toolchain.
95import os
96java_home = '/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home'
97android_sdk = Path(dict(line.split('=', 1) for line in (ROOT / 'android/local.properties').read_text().splitlines() if '=' in line)['sdk.dir'])
98checker = sorted((android_sdk / 'build-tools').glob('*/apksigner'))[-1]
99signature = 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)
101certificate = re.search(r'Signer #1 certificate SHA-256 digest: ([a-f0-9]+)', signature).group(1)
102for 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.
108ipa = RELEASE / f'CH-J-SM-Mobile-{VERSION}-{BUILD_ID}-unsigned.ipa'
109with 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)
114add(ipa, 'ipa', 'ios', 'arm64', 'unsigned')
115simulator = ROOT / 'build/ios/iphonesimulator/Runner.app'
116if 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')
122print(json.dumps(dict(release=str(RELEASE), buildId=BUILD_ID, files=len(entries), artifacts=len(artifacts))))
SHA-256f811f21c261a3eab38be61b3e30694a7729803bcc95b7a29d4ba35708addac1e

CH-J Proprietary Software License 1.14 · 소스는 CH-J Proprietary Software License 1.14에 따라 제공됩니다. 소스를 공개한다고 해서 라이선스 조항이 바뀌거나 추가 권리가 부여되지는 않습니다.

압축 파일 SHA-25646e427a43e87af091004f36145590bda8fe90086dfb0f631afeb5029fb0b5655
파일