陶泥儿产物按平台发行包上限预检
- build.rs 保留 512 MiB 读取安全线,另按 shared-contracts 的 GAME_DISTRIBUTION_MAX_PACKAGE_BYTES(200 MiB)做预检 - 构建收尾与发布读包两处都预检,超限给「精简资源后重新导出再发布」,不再等上传吃服务端 413 - 新增平台上限单测,并锁定读取安全线必须高于平台上限
This commit is contained in:
@@ -35,8 +35,24 @@ const OUTPUT_TAIL_MAX_CHARS: usize = 4_000;
|
||||
const ARTIFACT_ENTRY_FILE_NAME: &str = "index.html";
|
||||
/// 读取产物时单次读入的字节数。sha256 与字节缓冲共用这一遍流式读取。
|
||||
const PACKAGE_READ_CHUNK_BYTES: usize = 64 * 1024;
|
||||
/// 产物 zip 的合理上限:与试玩包同一口径(512 MiB),超过它直接拒绝读入内存。
|
||||
const ARTIFACT_PACKAGE_MAX_BYTES: u64 = 512 * 1024 * 1024;
|
||||
/// 读取产物时的体积安全线:超过它直接拒绝读入内存(与旧本地发行包读取口径一致)。
|
||||
const ARTIFACT_PACKAGE_READ_MAX_BYTES: u64 = 512 * 1024 * 1024;
|
||||
|
||||
/// 平台发行包上限(200 MiB):单一来源是 `shared-contracts`,这里只做发布前的同口径预检。
|
||||
///
|
||||
/// 与上一条读取安全线不是一个概念:安全线管「能不能读进内存」,这里管「平台上能不能收」。
|
||||
/// 构建收尾与发布读包两处都过它,避免作者等完一轮再吃服务端 413。
|
||||
fn ensure_within_platform_package_limit(size_bytes: u64) -> Result<(), String> {
|
||||
let limit = shared_contracts::game_distribution::GAME_DISTRIBUTION_MAX_PACKAGE_BYTES;
|
||||
if size_bytes > limit {
|
||||
return Err(format!(
|
||||
"发行包 {:.1} MiB 超过平台上限 {} MiB;请精简资源后重新导出再发布",
|
||||
size_bytes as f64 / (1024.0 * 1024.0),
|
||||
limit / (1024 * 1024)
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 固定产物读出来的包:发布链路只需要这几项事实,不需要逐条文件摘要。
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -164,12 +180,14 @@ fn conclude(
|
||||
&& !metadata.file_type().is_symlink()
|
||||
&& metadata.len() > 0
|
||||
);
|
||||
// zip 规范:根目录必须有 `index.html`。脚本"成功"但产物不是合规 zip(或写到了别处),
|
||||
// 与没有产物同类,都报 artifactMissing。
|
||||
// zip 规范:根目录必须有 `index.html`,且包体不得超过平台发行包上限。脚本"成功"但产物
|
||||
// 不合规(或写到了别处),与没有产物同类,都报 artifactMissing。
|
||||
let bytes = std::fs::read(&path).ok();
|
||||
let conformant = produced
|
||||
&& std::fs::read(&path)
|
||||
.map(|bytes| inspect_artifact(&bytes).is_ok())
|
||||
.unwrap_or(false);
|
||||
&& bytes.as_deref().is_some_and(|bytes| {
|
||||
ensure_within_platform_package_limit(bytes.len() as u64).is_ok()
|
||||
&& inspect_artifact(bytes).is_ok()
|
||||
});
|
||||
if !conformant {
|
||||
return Err(TaonierExportBuildError::ArtifactMissing(
|
||||
TaonierArtifactMissing {
|
||||
@@ -201,13 +219,14 @@ pub(crate) fn read_artifact_package(root: &Path) -> Result<TaonierArtifactPackag
|
||||
if metadata.len() == 0 {
|
||||
return Err("陶泥儿产物为空".to_string());
|
||||
}
|
||||
if metadata.len() > ARTIFACT_PACKAGE_MAX_BYTES {
|
||||
if metadata.len() > ARTIFACT_PACKAGE_READ_MAX_BYTES {
|
||||
return Err(format!(
|
||||
"陶泥儿产物大小超出上限:{}B > {}B",
|
||||
metadata.len(),
|
||||
ARTIFACT_PACKAGE_MAX_BYTES
|
||||
ARTIFACT_PACKAGE_READ_MAX_BYTES
|
||||
));
|
||||
}
|
||||
ensure_within_platform_package_limit(metadata.len())?;
|
||||
|
||||
let (package_bytes, package_sha256) = read_package_bytes(&path)?;
|
||||
if package_bytes.len() as u64 != metadata.len() {
|
||||
@@ -240,10 +259,10 @@ fn read_package_bytes(path: &Path) -> Result<(Vec<u8>, String), String> {
|
||||
}
|
||||
hasher.update(&buffer[..read]);
|
||||
bytes.extend_from_slice(&buffer[..read]);
|
||||
if bytes.len() as u64 > ARTIFACT_PACKAGE_MAX_BYTES {
|
||||
if bytes.len() as u64 > ARTIFACT_PACKAGE_READ_MAX_BYTES {
|
||||
return Err(format!(
|
||||
"陶泥儿产物大小超出上限:{}B",
|
||||
ARTIFACT_PACKAGE_MAX_BYTES
|
||||
ARTIFACT_PACKAGE_READ_MAX_BYTES
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -489,6 +508,21 @@ mod tests {
|
||||
assert_eq!(unavailable.cause, "命令沙箱不可用");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn platform_package_limit_is_the_shared_contract_and_reports_mib() {
|
||||
let limit = shared_contracts::game_distribution::GAME_DISTRIBUTION_MAX_PACKAGE_BYTES;
|
||||
assert!(ensure_within_platform_package_limit(limit).is_ok());
|
||||
// 读取安全线(512 MiB)与平台上限(200 MiB)不是一个概念:这里必须卡平台上限。
|
||||
assert!(limit < ARTIFACT_PACKAGE_READ_MAX_BYTES);
|
||||
let error =
|
||||
ensure_within_platform_package_limit(limit + 1).expect_err("超过平台上限必须失败");
|
||||
assert!(
|
||||
error.contains("超过平台上限"),
|
||||
"错误文案应说明超限:{error}"
|
||||
);
|
||||
assert!(error.contains("200 MiB"), "错误文案应带上平台上限:{error}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reads_the_fixed_artifact_with_hash_size_and_file_count() {
|
||||
let root = fixture_root("read-package");
|
||||
|
||||
Reference in New Issue
Block a user