chore: import verified syly2 maintenance baseline without embedded credentials

This commit is contained in:
Ethan
2026-10-29 22:22:49 +08:00
commit da8224efd8
5265 changed files with 1199963 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
> 历史记录:以下说明对应 2026-10-29 原始 UAT 构建及部署,当前仓库的构建入口和密钥配置请看根目录 README。
# syly2 UAT 编译工具
目标主机:`ubuntu@192.168.68.223`。这些工具用于隔离的源码编译,不包含数据库初始化或应用发布步骤。
## 本次目录
- 工具链:`/home/ubuntu/.local/opt/syly2-build`
- Maven 专用仓库:`/home/ubuntu/.local/share/syly2-build/m2`
- 构建记录:`/home/ubuntu/builds/syly2/20261029-01`
- 编译副本:上述构建目录下的 `source`
- 构建日志:上述构建目录下的 `logs`
- 本地传输包、原始文件哈希清单及回收证据:项目 `.local/uat-build/20261029-01`
## 工具职责
1. `prepare-toolchain.py`:安装固定版本 Corretto 8.512.09.1 与 Maven 3.9.11,验证官方 SHA-256/SHA-512 校验值;只写入当前用户的专用目录,不修改系统 Java 或 shell 配置。
2. `install-private-libs.py RUN_DIRECTORY`:将交接包中的 6 个旧 JAR 注册到专用 Maven 仓库,记录原文件哈希。JODConverter 使用原项目声明的坐标和交接包中的 JAR,其原嵌入坐标不同,未据此升级或替换组件。
3. `apply-compatibility.py RUN_DIRECTORY`:校验原始文件哈希,备份并修改编译副本中的三个文件:POM 的 Windows bootclasspath 分号改为 `${path.separator}`,两个统计 Controller 各删除一条未使用的 JDK 内部类 import。保留原换行符,并输出完整 `compatibility.patch`。`pom-linux.patch` 单独展示第一项差异。
4. `run-build.sh RUN_DIRECTORY PHASE`:在专用 JDK/仓库下执行 `clean package`,限制 JVM 内存、降低 CPU 调度优先级并记录退出码;显式跳过测试编译及执行。
5. `verify-build.py RUN_DIRECTORY PHASE`:校验构建退出码、WAR CRC、关键条目和 Java 字节码版本,核对源码差异,保存产物与哈希,并比较构建前后的共享 UAT 容器状态。需要构建目录中已有 `containers-before.json` 基线记录。
本次只打包外层工程的 `pom.xml`、`src`、`jar`,不带历史 `target` 或内层重复工程。传输包与展开后的 5,302 个文件均核验了 SHA-256。
## 重新编译当前 UAT 副本
```sh
ssh ubuntu@192.168.68.223 'bash /home/ubuntu/builds/syly2/20261029-01/scripts/run-build.sh /home/ubuntu/builds/syly2/20261029-01 repeat'
```
`repeat` 对应日志文件名;需要保留每次日志时请使用不同的阶段名。
仅使用编译工具链时:
```sh
source /home/ubuntu/.local/opt/syly2-build/env.sh
java -version
mvn -version
```
## 在新的构建目录复现
先建立当前用户所有、权限为 700 的独立目录,创建 `source`、`logs`、`artifacts`、`scripts` 子目录,复制并核验源码包、原始 `source-manifest.json` 与这些工具。然后执行:
```sh
python3 scripts/prepare-toolchain.py
python3 scripts/install-private-libs.py /absolute/path/to/build-run
python3 scripts/apply-compatibility.py /absolute/path/to/build-run
bash scripts/run-build.sh /absolute/path/to/build-run linux-build
```
源码包包含原有配置,构建目录、传输包和产物均应按受限资料管理。编译通过不代表应用启动、数据库恢复、JSP 运行或省级接口联调通过。
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""Apply only the three verified compilation fixes to an isolated source copy."""
import difflib
import hashlib
import json
import os
from pathlib import Path
import re
import sys
os.umask(0o077)
run = Path(sys.argv[1]).resolve()
source = run / 'source'
manifest = json.loads((run / 'source-manifest.json').read_text())
changes = {
'pom.xml': (
b'${java.home}/lib/rt.jar;${java.home}/lib/jce.jar',
b'${java.home}/lib/rt.jar${path.separator}${java.home}/lib/jce.jar',
),
'src/main/java/com/zzc/web/htoal/controller/HotelQuarterStatisticController.java': (
b'import com.sun.org.apache.regexp.internal.recompile;\r\n', b'',
),
'src/main/java/com/zzc/web/travel/controller/InlandStatisticPageController.java': (
b'import com.sun.org.apache.regexp.internal.recompile;\r\n', b'',
),
}
patches = []
for relative, (before, after) in changes.items():
path = source / relative
data = path.read_bytes()
backup = run / 'artifacts/original-build-files' / relative
if hashlib.sha256(data).hexdigest() == manifest[relative]:
assert data.count(before) == 1, relative
if relative.endswith('.java'):
assert len(re.findall(rb'\brecompile\b', data)) == 1, relative
backup.parent.mkdir(parents=True, exist_ok=True)
backup.write_bytes(data)
path.write_bytes(data.replace(before, after, 1))
elif relative == 'pom.xml' and (run / 'artifacts/pom-original.xml').exists():
original = (run / 'artifacts/pom-original.xml').read_bytes()
assert hashlib.sha256(original).hexdigest() == manifest[relative]
assert data == original.replace(before, after, 1)
backup.parent.mkdir(parents=True, exist_ok=True)
backup.write_bytes(original)
else:
assert backup.exists(), 'Unexpected source change: ' + relative
assert hashlib.sha256(backup.read_bytes()).hexdigest() == manifest[relative]
assert data == backup.read_bytes().replace(before, after, 1)
patches.extend(difflib.unified_diff(
backup.read_text().splitlines(keepends=True), path.read_text().splitlines(keepends=True),
fromfile='a/' + relative, tofile='b/' + relative,
))
print('Compatibility fix verified:', relative)
(run / 'artifacts/compatibility.patch').write_text(''.join(patches))
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Register the six supplied legacy JARs in a dedicated Maven repository."""
import hashlib
import json
import os
from pathlib import Path
import subprocess
import sys
import xml.etree.ElementTree as ET
os.umask(0o077)
run = Path(sys.argv[1]).resolve()
source = run / 'source'
repo = Path.home() / '.local/share/syly2-build/m2'
base = Path.home() / '.local/opt/syly2-build'
env = dict(os.environ, JAVA_HOME=str(base / 'corretto-8.512.09.1'))
env['PATH'] = env['JAVA_HOME'] + '/bin:' + env['PATH']
mvn = base / 'apache-maven-3.9.11/bin/mvn'
artifacts = [
('com.oracle', 'ojdbc14', '10.2.0.4.0', 'jar/ojdbc14/ojdbc14/10.2.0.4.0/ojdbc14-10.2.0.4.0.jar'),
*[('com.ckfinder', name, '2.4', f'jar/ckfinder/ckfinder/{name}/2.4/{name}-2.4.jar') for name in [
'CKFinder-updateByAlexander', 'CKFinderPlugin-FileEditor',
'CKFinderPlugin-ImageResize', 'CKFinderPlugin-Watermark',
]],
('com.artofsolving', 'jodconverter', '3.0-beta-4', 'src/main/webapp/WEB-INF/lib/jodconverter-3.0-beta-4.jar'),
]
evidence = []
poms = run / 'artifacts/local-poms'
poms.mkdir(exist_ok=True)
for group, artifact, version, relative in artifacts:
jar = source / relative
original_pom = jar.with_suffix('.pom')
if original_pom.is_file():
pom = original_pom
else:
project = ET.Element('project', xmlns='http://maven.apache.org/POM/4.0.0')
for tag, text in [('modelVersion', '4.0.0'), ('groupId', group), ('artifactId', artifact), ('version', version), ('packaging', 'jar')]:
ET.SubElement(project, tag).text = text
pom = poms / f'{artifact}-{version}.pom'
ET.ElementTree(project).write(pom, encoding='utf-8', xml_declaration=True)
log = run / 'logs' / f'install-{artifact}.log'
with log.open('w') as output:
result = subprocess.run([
str(mvn), '-B', '-ntp', f'-Dmaven.repo.local={repo}',
'org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file',
f'-Dfile={jar}', f'-DpomFile={pom}',
f'-DgroupId={group}', f'-DartifactId={artifact}', f'-Dversion={version}', '-Dpackaging=jar',
], cwd=run, env=env, stdout=output, stderr=subprocess.STDOUT, timeout=300)
if result.returncode:
raise RuntimeError(f'Installation failed: {artifact}; see {log}')
evidence.append({'coordinates': f'{group}:{artifact}:{version}', 'source': relative, 'sha256': hashlib.sha256(jar.read_bytes()).hexdigest()})
print('Installed', evidence[-1]['coordinates'], flush=True)
(run / 'artifacts/private-libs.json').write_text(json.dumps(evidence, indent=2) + '\n')
+11
View File
@@ -0,0 +1,11 @@
--- a/pom.xml
+++ b/pom.xml
@@ -812,7 +812,7 @@
</compilerArgs>
<compilerArguments>
<verbose/>
- <bootclasspath>${java.home}/lib/rt.jar;${java.home}/lib/jce.jar</bootclasspath>
+ <bootclasspath>${java.home}/lib/rt.jar${path.separator}${java.home}/lib/jce.jar</bootclasspath>
</compilerArguments>
<fork>true</fork>
</configuration>
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
"""Install the pinned syly2 build toolchain in the current UAT user's home."""
import hashlib
import json
import os
from pathlib import Path
import subprocess
import tarfile
import urllib.request
os.umask(0o077)
base = Path.home() / '.local/opt/syly2-build'
base.mkdir(parents=True, exist_ok=True)
downloads = base / 'downloads'
downloads.mkdir(exist_ok=True)
packages = [
{
'name': 'corretto-8.512.09.1',
'url': 'https://corretto.aws/downloads/resources/8.512.09.1/amazon-corretto-8.512.09.1-linux-x64.tar.gz',
'algorithm': 'sha256',
'checksum': '2f2df938886e61c80010ed5af0ea6504ca685280a3b807c5c59ed3ca4fb95be4',
'checksum_source': 'https://corretto.aws/downloads/latest_sha256/amazon-corretto-8-x64-linux-jdk.tar.gz',
'executable': 'bin/java',
},
{
'name': 'apache-maven-3.9.11',
'url': 'https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.tar.gz',
'algorithm': 'sha512',
'checksum': 'bcfe4fe305c962ace56ac7b5fc7a08b87d5abd8b7e89027ab251069faebee516b0ded8961445d6d91ec1985dfe30f8153268843c89aa392733d1a3ec956c9978',
'checksum_source': 'https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.tar.gz.sha512',
'executable': 'bin/mvn',
},
]
for package in packages:
archive = downloads / (package['name'] + '.tar.gz')
if not archive.exists():
print('Downloading', package['name'], flush=True)
partial = archive.with_suffix('.part')
with urllib.request.urlopen(package['url'], timeout=90) as response, partial.open('wb') as output:
while chunk := response.read(1024 * 1024):
output.write(chunk)
partial.rename(archive)
digest = hashlib.new(package['algorithm'], archive.read_bytes()).hexdigest()
if digest != package['checksum']:
raise RuntimeError('Checksum mismatch: ' + package['name'])
dest = base / package['name']
if not dest.exists():
staging = base / (package['name'] + '.extract')
staging.mkdir(exist_ok=False)
with tarfile.open(archive) as tar:
tar.extractall(staging, filter='data')
children = list(staging.iterdir())
if len(children) != 1 or not children[0].is_dir():
raise RuntimeError('Unexpected archive layout')
children[0].rename(dest)
staging.rmdir()
if not (dest / package['executable']).is_file():
raise RuntimeError('Incomplete installation: ' + str(dest))
print('Verified and installed', dest, flush=True)
jdk = base / packages[0]['name']
maven = base / packages[1]['name']
env_text = f'export JAVA_HOME="{jdk}"\nexport PATH="{jdk}/bin:{maven}/bin:$PATH"\nexport MAVEN_OPTS="-Xmx1536m -Djava.awt.headless=true"\n'
(base / 'env.sh').write_text(env_text)
(base / 'manifest.json').write_text(json.dumps(packages, indent=2) + '\n')
env = dict(os.environ, JAVA_HOME=str(jdk), PATH=f'{jdk}/bin:{maven}/bin:' + os.environ['PATH'])
subprocess.run([str(jdk / 'bin/java'), '-version'], env=env, check=True)
subprocess.run([str(maven / 'bin/mvn'), '-version'], env=env, check=True)
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
umask 077
run_dir="${1:?Usage: run-build.sh RUN_DIRECTORY PHASE}"
phase="${2:?Missing build phase}"
case "$phase" in
*[!a-zA-Z0-9_-]*|'') printf 'Invalid phase\n' >&2; exit 2 ;;
esac
source "$HOME/.local/opt/syly2-build/env.sh"
mkdir -p "$run_dir/logs" "$HOME/.local/share/syly2-build/m2"
cd "$run_dir/source"
set +e
nice -n 10 timeout 1200 mvn --batch-mode --no-transfer-progress \
-Dstyle.color=never \
-Dmaven.repo.local="$HOME/.local/share/syly2-build/m2" \
-Dmaven.test.skip=true -DskipTests=true \
clean package > "$run_dir/logs/$phase.log" 2>&1
result=$?
set -e
printf '%s\n' "$result" > "$run_dir/logs/$phase.exit-code"
printf 'Build phase %s exited %s; log: %s\n' "$phase" "$result" "$run_dir/logs/$phase.log"
exit "$result"
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""Verify the WAR, record exact source changes, and compare shared UAT containers."""
import collections
import hashlib
import json
import os
from pathlib import Path
import shutil
import struct
import subprocess
import sys
import zipfile
os.umask(0o077)
run = Path(sys.argv[1]).resolve()
phase = sys.argv[2]
assert (run / 'logs' / (phase + '.exit-code')).read_text().strip() == '0'
log = (run / 'logs' / (phase + '.log')).read_text(errors='replace')
assert 'BUILD SUCCESS' in log
source = run / 'source'
war = source / 'target/syly.war'
required = [
'WEB-INF/web.xml',
'WEB-INF/classes/com/zzc/web/system/controller/core/LoginController.class',
'WEB-INF/classes/com/zzc/core/interceptors/AuthInterceptor.class',
'WEB-INF/classes/com/zzc/web/htoal/controller/HotelMonthlyController.class',
]
with zipfile.ZipFile(war) as archive:
bad = archive.testzip()
assert bad is None, bad
names = archive.namelist()
assert all(name in names for name in required)
classes = [name for name in names if name.startswith('WEB-INF/classes/') and name.endswith('.class')]
major_versions = collections.Counter(struct.unpack('>H', archive.read(name)[6:8])[0] for name in classes)
assert set(major_versions) == {52}, major_versions
libs = [name for name in names if name.startswith('WEB-INF/lib/') and name.endswith('.jar')]
manifest = json.loads((run / 'source-manifest.json').read_text())
changes = []
for relative, expected in manifest.items():
actual = hashlib.sha256((source / relative).read_bytes()).hexdigest()
if expected != actual:
changes.append({'path': relative, 'original_sha256': expected, 'build_sha256': actual})
expected_changes = {
'pom.xml',
'src/main/java/com/zzc/web/htoal/controller/HotelQuarterStatisticController.java',
'src/main/java/com/zzc/web/travel/controller/InlandStatisticPageController.java',
}
assert {change['path'] for change in changes} == expected_changes, changes
ids = subprocess.check_output(['docker', 'ps', '-aq'], text=True).split()
fmt = '{"id":{{json .Id}},"name":{{json .Name}},"startedAt":{{json .State.StartedAt}},"running":{{json .State.Running}},"restartCount":{{json .RestartCount}}}'
after = [json.loads(subprocess.check_output(['docker', 'inspect', '--format', fmt, cid], text=True)) for cid in ids]
before = json.loads((run / 'containers-before.json').read_text())
key = lambda values: {value['id']: value for value in values}
unchanged = key(before) == key(after)
(run / 'containers-after.json').write_text(json.dumps(after, indent=2) + '\n')
artifact = run / 'artifacts/syly.war'
shutil.copyfile(war, artifact)
artifact.chmod(0o600)
evidence = {
'host': '192.168.68.223', 'run': str(run), 'phase': phase, 'build_success': True,
'artifact': str(artifact), 'artifact_bytes': artifact.stat().st_size,
'artifact_sha256': hashlib.sha256(artifact.read_bytes()).hexdigest(),
'zip_crc_check': 'passed', 'required_entries_present': required,
'compiled_class_files': len(classes), 'class_major_versions': dict(major_versions), 'bundled_jars': len(libs),
'main_java_sources': len(list((source / 'src/main/java').rglob('*.java'))),
'extra_java_sources_outside_maven_root': len(list((source / 'src/main/com').rglob('*.java'))),
'source_manifest_files': len(manifest), 'changed_source_files': changes,
'tests_skipped': 'Tests are skipped' in log,
'containers_before': len(before), 'containers_after': len(after), 'existing_containers_unchanged': unchanged,
'built_at': next((line.split('Finished at:', 1)[1].strip() for line in log.splitlines() if 'Finished at:' in line), None),
'elapsed': next((line.split('Total time:', 1)[1].strip() for line in log.splitlines() if 'Total time:' in line), None),
'java_version': subprocess.check_output([str(Path.home() / '.local/opt/syly2-build/corretto-8.512.09.1/bin/java'), '-version'], stderr=subprocess.STDOUT, text=True).strip(),
}
(run / 'artifacts/verification.json').write_text(json.dumps(evidence, indent=2) + '\n')
print(json.dumps(evidence, indent=2))
if not unchanged:
print('NOTICE: Container inventory changed during the build; inspect before/after records.')