chore: import verified syly2 maintenance baseline without embedded credentials
This commit is contained in:
@@ -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 运行或省级接口联调通过。
|
||||
@@ -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))
|
||||
@@ -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')
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
@@ -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.')
|
||||
Reference in New Issue
Block a user