fix(repo):统一发布健康与只读质量门禁
bat-rust / Build and test Rust (push) Canceled after 0s
bat-rust / Build and test Go API (push) Canceled after 0s

This commit is contained in:
2026-09-13 22:09:44 +08:00
parent 32fc64fa83
commit c17904ee1c
25 changed files with 869 additions and 184 deletions
+68 -23
View File
@@ -55,17 +55,33 @@ type GameMainConfigSummary struct {
DefaultConnectionGroup string `json:"default_connection_group,omitempty"`
}
// DistributionHealth is the release-level authorization used by read paths.
//
// In RPC mode Ready is copied from Rust's release.status
// official_distribution_ready fact. The local manifest checks only establish
// that this process has a usable read snapshot; they do not replace Rust's
// release verifier.
type DistributionHealth struct {
Ready bool `json:"ready"`
Source string `json:"source"`
Status string `json:"status,omitempty"`
StatusCode string `json:"status_code,omitempty"`
IntegrityStatus string `json:"integrity_status,omitempty"`
Diagnostics []string `json:"diagnostics,omitempty"`
}
// ReleaseIndex is the in-memory view of a published resource root.
type ReleaseIndex struct {
mu sync.RWMutex
ResourceRoot string `json:"resource_root"`
Source string `json:"source"` // "rpc" | "resource_root" | "empty"
RPCAvailable bool `json:"rpc_available"`
DoctorHealthy *bool `json:"doctor_healthy,omitempty"`
Snapshot *SnapshotSummary `json:"snapshot,omitempty"`
ManifestVersion int `json:"manifest_version,omitempty"`
Entries []ResourceEntry `json:"entries"`
ResourceRoot string `json:"resource_root"`
Source string `json:"source"` // "rpc" | "resource_root" | "empty"
RPCAvailable bool `json:"rpc_available"`
DoctorHealthy *bool `json:"doctor_healthy,omitempty"`
Distribution DistributionHealth `json:"distribution"`
Snapshot *SnapshotSummary `json:"snapshot,omitempty"`
ManifestVersion int `json:"manifest_version,omitempty"`
Entries []ResourceEntry `json:"entries"`
// byRel maps relative path (host/path...) to entry index.
byRel map[string]int
// MissingOnDisk lists relative paths present in the index but absent on disk.
@@ -74,16 +90,17 @@ type ReleaseIndex struct {
// Summary returns a JSON-serializable overview without the full entry list.
type ReleaseSummary struct {
ResourceRoot string `json:"resource_root"`
Source string `json:"source"`
RPCAvailable bool `json:"rpc_available"`
DoctorHealthy *bool `json:"doctor_healthy,omitempty"`
Snapshot *SnapshotSummary `json:"snapshot,omitempty"`
ManifestVersion int `json:"manifest_version,omitempty"`
EntryCount int `json:"entry_count"`
PresentCount int `json:"present_count"`
MissingCount int `json:"missing_count"`
Ready bool `json:"ready"`
ResourceRoot string `json:"resource_root"`
Source string `json:"source"`
RPCAvailable bool `json:"rpc_available"`
DoctorHealthy *bool `json:"doctor_healthy,omitempty"`
Distribution DistributionHealth `json:"distribution"`
Snapshot *SnapshotSummary `json:"snapshot,omitempty"`
ManifestVersion int `json:"manifest_version,omitempty"`
EntryCount int `json:"entry_count"`
PresentCount int `json:"present_count"`
MissingCount int `json:"missing_count"`
Ready bool `json:"ready"`
}
// Summary builds a compact release overview.
@@ -96,20 +113,32 @@ func (idx *ReleaseIndex) Summary() ReleaseSummary {
present++
}
}
distribution := idx.Distribution
distribution.Diagnostics = append([]string(nil), idx.Distribution.Diagnostics...)
localComplete := idx.ResourceRoot != "" && len(idx.Entries) > 0 && present == len(idx.Entries)
// Hand-built indexes are retained for compatibility with local tests and
// diagnostics. Any index explicitly sourced from RPC must carry the Rust
// health fact; an RPC index without it is never considered distributable.
if distribution.Source == "" {
distribution.Ready = localComplete && idx.Source != "rpc" && idx.Source != "rpc+local_manifest"
} else {
distribution.Ready = distribution.Ready && localComplete
}
return ReleaseSummary{
ResourceRoot: idx.ResourceRoot,
Source: idx.Source,
RPCAvailable: idx.RPCAvailable,
DoctorHealthy: idx.DoctorHealthy,
Distribution: distribution,
Snapshot: idx.Snapshot,
ManifestVersion: idx.ManifestVersion,
EntryCount: len(idx.Entries),
PresentCount: present,
MissingCount: len(idx.MissingOnDisk),
// A release is distributable only when every manifest entry is present
// and has the expected size. Serving a partial release can leave clients
// with an apparently valid bootstrap and an unrecoverable download set.
Ready: idx.ResourceRoot != "" && len(idx.Entries) > 0 && present == len(idx.Entries),
// A release is distributable only when Rust authorizes it and every
// entry in this process's read snapshot is usable. Serving a partial
// release can leave clients with an unrecoverable download set.
Ready: distribution.Ready,
}
}
@@ -158,6 +187,7 @@ func BuildIndexFromManifestEntries(
snapshot *SnapshotSummary,
manifestVersion int,
entries []manifestEntry,
distribution DistributionHealth,
) (*ReleaseIndex, error) {
rootAbs, err := filepath.Abs(resourceRoot)
if err != nil {
@@ -168,7 +198,8 @@ func BuildIndexFromManifestEntries(
Source: source,
RPCAvailable: rpcAvailable,
DoctorHealthy: doctorHealthy,
Snapshot: snapshot,
Distribution: distribution,
Snapshot: snapshotWithDistributionHealth(snapshot, distribution),
ManifestVersion: manifestVersion,
byRel: make(map[string]int),
}
@@ -258,7 +289,21 @@ func LoadIndexFromResourceRoot(resourceRoot string) (*ReleaseIndex, error) {
}
}
}
return BuildIndexFromManifestEntries(rootAbs, "resource_root", false, nil, snapshot, manifest.Version, entries)
return BuildIndexFromManifestEntries(
rootAbs,
"resource_root",
false,
nil,
snapshot,
manifest.Version,
entries,
DistributionHealth{
Ready: true,
Source: "resource_root_override",
Status: "ready",
StatusCode: "resource_root.ready",
},
)
}
type manifestEntry struct {