CH-J SM Mobileserverhantering över SSH
Menyn
Publicerad källa

CH-J SM Mobile

Bläddra i kataloger och filer för en specifik programutgåva.

Ladda ner källa ZIP
CH-J / SM Mobile 134 filer
sm-mobile-20261016T050549Z-3678f2a05fa9 Tillagd: · Byggd:
Filer
prepare_release.py Tillagd: Datum inte noterat 7,3 KB · 122 linjer
Ladda ner fil
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 · Källan finns under CH-J Proprietary Software License 1.14. Dess tillgänglighet ändrar inte licensvillkoren eller ger ytterligare rättigheter.

Arkiv SHA-256c486a807e4828998b9c57ab0f2e760f7980b31965fe118e89d3e1a5a2b65be65