Compare commits

..

3 Commits

Author SHA1 Message Date
houseme 042d817340 Merge branch 'main' into overtrue/activate-data-usage-e2e 2026-08-23 12:08:51 +08:00
overtrue 590b3d09bb test(e2e): bind data usage selection to Linux listing 2026-08-23 08:52:46 +08:00
overtrue 308b01869e test(e2e): activate data usage regressions 2026-08-23 06:29:54 +08:00
10 changed files with 9 additions and 387 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
sha256-darwin=9f767b37ed8b1c82da62ea441462d75487785c8086e56f08fb6f6cd89c6e2e52
sha256-linux=fbdaf42b220958d4b1e8880e0f8b5a7992d38e21051bb60596dd4538424757d6
sha256-darwin=c88d762eb265fd90f467e57c948a7e6806d5918dc75798651afe120af85c9d07
sha256-linux=9b694893afb2d750b37f08e2a4fe229b37cc9d21332985a3727daf5ebd77f1a9
+1 -2
View File
@@ -30,8 +30,7 @@ make build-docker BUILD_OS=ubuntu22.04
- Crate membership: `Cargo.toml` `[workspace].members`
- Architecture, layering, crate map: [ARCHITECTURE.md](ARCHITECTURE.md)
- Migration guardrails & readiness contracts: [docs/architecture/](docs/architecture/README.md)
- CI workflow steps: `.github/workflows/`; event, timeout, and required-status
matrix: [docs/testing/ci-gates.md](docs/testing/ci-gates.md)
- CI gates: `.github/workflows/ci.yml` (source of truth; never copy its steps into docs)
- Test-layer taxonomy, per-layer entry commands, serial/nextest rules, flake
policy: [docs/testing/README.md](docs/testing/README.md)
- Tier/ILM transition debugging (xl.meta inspection, versionId tracing):
-2
View File
@@ -70,8 +70,6 @@ make pre-pr
> For the full test-layer taxonomy (unit / ecstore black-box / e2e / s3s-e2e / S3 compatibility / chaos / fuzz / bench), each layer's entry command, the naming conventions the migration gate depends on, and the serial/nextest rules, see [docs/testing/README.md](docs/testing/README.md).
> For the event, timeout, required-status, and local reproduction matrix, see [docs/testing/ci-gates.md](docs/testing/ci-gates.md).
### 🔒 Automated Pre-commit Hooks
#### What `make pre-commit` and `make pre-pr` actually run
+4 -14
View File
@@ -59,7 +59,6 @@ where
/// Regression test for data usage accuracy (issue #1012).
/// Launches rustfs, writes 1000 objects, then asserts admin data usage reports the full count.
#[tokio::test(flavor = "multi_thread")]
#[ignore = "Starts a rustfs server and requires awscurl; enable when running full E2E"]
async fn data_usage_reports_all_objects() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
init_logging();
@@ -86,28 +85,20 @@ async fn data_usage_reports_all_objects() -> Result<(), Box<dyn std::error::Erro
usage
.buckets_usage
.get(TEST_BUCKET)
.map(|bucket_usage| usage.objects_total_count >= 1000 && bucket_usage.objects_count >= 1000)
.map(|bucket_usage| usage.objects_total_count == 1000 && bucket_usage.objects_count == 1000)
.unwrap_or(false)
})
.await?;
// Assert total object count and per-bucket count are not truncated
// Assert total object count and per-bucket count are exact.
let bucket_usage = usage
.buckets_usage
.get(TEST_BUCKET)
.cloned()
.expect("bucket usage should exist");
assert!(
usage.objects_total_count >= 1000,
"total object count should be at least 1000, got {}",
usage.objects_total_count
);
assert!(
bucket_usage.objects_count >= 1000,
"bucket object count should be at least 1000, got {}",
bucket_usage.objects_count
);
assert_eq!(usage.objects_total_count, 1000, "total object count should be exact");
assert_eq!(bucket_usage.objects_count, 1000, "bucket object count should be exact");
env.stop_server();
Ok(())
@@ -116,7 +107,6 @@ async fn data_usage_reports_all_objects() -> Result<(), Box<dyn std::error::Erro
/// Regression test for issue #3898.
/// Versioned buckets should expose versions and delete markers through admin data usage.
#[tokio::test(flavor = "multi_thread")]
#[ignore = "Starts a rustfs server and requires awscurl; enable when running full E2E"]
async fn data_usage_reports_versioned_objects_and_delete_markers() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
init_logging();
-2
View File
@@ -61,8 +61,6 @@ pub mod runtime_config;
pub mod scanner;
pub mod scanner_budget;
pub mod scanner_folder;
#[cfg(test)]
mod scanner_heal_admission_baseline;
pub mod scanner_io;
pub mod sleeper;
pub(crate) mod storage_api;
@@ -1,185 +0,0 @@
//! Executable Phase-0 contract for the scanner/heal overlap investigation.
//!
//! These tests model the matrix that a future storage-owned admission
//! primitive must satisfy. They intentionally do not provide a production
//! lock or coordinator; the issue's current evidence establishes a baseline,
//! not a demonstrated stale-writer failure.
#[cfg(test)]
mod tests {
const SCANNER_IO_SOURCE: &str = include_str!("scanner_io/io_disk.rs");
const SCANNER_FOLDER_SOURCE: &str = include_str!("scanner_folder.rs");
const HEAL_AUTO_SCAN_SOURCE: &str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../heal/src/heal/manager/auto_scan.rs"));
const HEAL_OBJECT_SOURCE: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../ecstore/src/set_disk/ops/heal.rs"));
const SET_LOCKING_SOURCE: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../ecstore/src/set_disk/ops/locking.rs"));
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Operation {
ScannerRead,
HealRead,
HealWrite,
DataMovementWrite,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct BaselineSample {
set: &'static str,
operation: Operation,
latency_us: u64,
backlog_depth: usize,
deferred: bool,
}
fn p99_latency(samples: &[BaselineSample]) -> u64 {
assert!(!samples.is_empty());
let mut latencies = samples.iter().map(|sample| sample.latency_us).collect::<Vec<_>>();
latencies.sort_unstable();
let rank = (latencies.len() * 99).div_ceil(100).saturating_sub(1);
latencies[rank]
}
fn restart_degraded_fixture() -> [BaselineSample; 8] {
[
BaselineSample {
set: "pool0/set0",
operation: Operation::ScannerRead,
latency_us: 120,
backlog_depth: 1,
deferred: false,
},
BaselineSample {
set: "pool0/set0",
operation: Operation::HealRead,
latency_us: 180,
backlog_depth: 1,
deferred: false,
},
BaselineSample {
set: "pool0/set0",
operation: Operation::HealWrite,
latency_us: 420,
backlog_depth: 2,
deferred: true,
},
BaselineSample {
set: "pool0/set0",
operation: Operation::ScannerRead,
latency_us: 160,
backlog_depth: 2,
deferred: false,
},
BaselineSample {
set: "pool0/set1",
operation: Operation::ScannerRead,
latency_us: 110,
backlog_depth: 0,
deferred: false,
},
BaselineSample {
set: "pool0/set1",
operation: Operation::HealRead,
latency_us: 150,
backlog_depth: 0,
deferred: false,
},
BaselineSample {
set: "pool0/set1",
operation: Operation::HealWrite,
latency_us: 360,
backlog_depth: 1,
deferred: true,
},
BaselineSample {
set: "pool0/set1",
operation: Operation::ScannerRead,
latency_us: 130,
backlog_depth: 1,
deferred: false,
},
]
}
fn same_set(a: &str, b: &str) -> bool {
a == b
}
fn may_overlap(left: Operation, right: Operation, same_set: bool) -> bool {
if !same_set {
return true;
}
matches!(
(left, right),
(Operation::ScannerRead, Operation::HealRead) | (Operation::HealRead, Operation::ScannerRead)
)
}
#[test]
fn scanner_heal_matrix_allows_read_read_and_blocks_heal_write() {
assert!(may_overlap(Operation::ScannerRead, Operation::HealRead, true));
assert!(!may_overlap(Operation::ScannerRead, Operation::HealWrite, true));
assert!(!may_overlap(Operation::DataMovementWrite, Operation::HealRead, true));
}
#[test]
fn scanner_heal_different_sets_remain_concurrent() {
assert!(may_overlap(
Operation::HealWrite,
Operation::ScannerRead,
same_set("pool0/set0", "pool0/set1")
));
}
#[test]
fn scanner_heal_restart_and_clock_skew_do_not_accept_old_owner() {
let old_owner_generation = 3_u64;
let restarted_generation = 4_u64;
let persisted_timestamp = 100_u64;
let observed_timestamp = 90_u64;
assert_ne!(old_owner_generation, restarted_generation);
assert!(observed_timestamp < persisted_timestamp);
}
#[test]
fn scanner_heal_overlap_inventory_has_no_unprotected_destructive_entry() {
// Keep the Phase-0 inventory tied to real entry points. The assertions
// deliberately check that the documented guards still exist; they do
// not claim that a shared admission primitive already exists.
assert!(SCANNER_IO_SOURCE.contains("let _guard = self.start_scan()"));
assert!(SCANNER_IO_SOURCE.contains("scan_data_folder"));
assert!(SCANNER_FOLDER_SOURCE.contains("send_required_scanner_heal_request"));
assert!(SCANNER_FOLDER_SOURCE.contains("update_pending_scanner_heal_after_admission"));
assert!(HEAL_AUTO_SCAN_SOURCE.contains("active_heals"));
assert!(HEAL_AUTO_SCAN_SOURCE.contains("contains_erasure_set"));
assert!(HEAL_OBJECT_SOURCE.contains("heal_object"));
assert!(HEAL_OBJECT_SOURCE.contains("get_write_lock"));
assert!(SET_LOCKING_SOURCE.contains("scanning_disks"));
assert!(SET_LOCKING_SOURCE.contains("new_disks.extend(scanning_disks)"));
}
#[test]
fn scanner_heal_admission_benchmark_degraded_quorum() {
let samples = restart_degraded_fixture();
assert_eq!(p99_latency(&samples), 420);
assert!(
samples
.iter()
.any(|sample| sample.operation == Operation::HealWrite && sample.deferred)
);
assert!(samples.iter().any(|sample| sample.set == "pool0/set1" && !sample.deferred));
assert_eq!(samples.iter().map(|sample| sample.backlog_depth).max(), Some(2));
}
#[test]
fn scanner_heal_set_deferral_preserves_quorum_and_backlog() {
let samples = restart_degraded_fixture();
let deferred_count = samples.iter().filter(|sample| sample.deferred).count();
let independent_progress = samples
.iter()
.filter(|sample| sample.set == "pool0/set1" && !sample.deferred)
.count();
assert_eq!(deferred_count, 2);
assert_eq!(independent_progress, 3);
assert!(samples.iter().all(|sample| sample.backlog_depth <= 2));
}
}
-1
View File
@@ -54,7 +54,6 @@ Two rules keep this directory healthy:
- [ecstore-config-consumer-inventory.md](ecstore-config-consumer-inventory.md)
- [obs-ecstore-dependency-inventory.md](obs-ecstore-dependency-inventory.md)
- [background-services-inventory.md](background-services-inventory.md)
- [scanner-heal-admission.md](scanner-heal-admission.md)
- [admin-route-action-snapshot.md](admin-route-action-snapshot.md)
- [compat-cleanup-register.md](compat-cleanup-register.md)
@@ -1,29 +0,0 @@
# Scanner/Heal admission Phase 0 baseline
This document records the current entry points and safety boundaries for backlog #1939. It is an inventory and test contract, not a lease design. No cluster-wide coordinator or second generation token is introduced until a deterministic benchmark demonstrates an SLO or stale-write failure.
## Entry-point inventory
| Work | Entry point | I/O and current guard | Fallback/namespace semantics |
| --- | --- | --- | --- |
| Scanner read/list | `crates/scanner/src/scanner_io/io_disk.rs:nsscanner_disk` | Per-disk `start_scan()` guard; bucket lifecycle/replication/object-lock reads precede `scan_data_folder` | Scanner keeps its local disk and durable cursor; no HealManager set-level admission is consulted |
| Scanner metadata read | `crates/scanner/src/scanner_folder.rs` object-size and metadata branches | Scanner cycle budget and per-disk scan marker | Corrupt metadata records the pending scanner ledger; MRF is an additional hint, not the durable owner |
| Scanner heal admission | `crates/scanner/src/scanner_folder.rs` `send_required_scanner_heal_request` | Existing manager queue dedup and pending ledger | MRF `Enqueued`/`Coalesced` is ledger-only; rejected MRF keeps immediate heal plus ledger |
| Heal auto scan | `crates/heal/src/heal/manager/auto_scan.rs` set admission loop | Queue-first then active-task check; replacement recovery blocklist | Scanning disks remain candidates when degraded quorum needs them; they are not globally excluded |
| Heal object read | `crates/ecstore/src/set_disk/ops/heal.rs` `heal_object` | Namespace write lock unless `no_lock`; reads file info before commit | Namespace lock is object-scoped and does not claim scanner cycle ownership |
| Disk selection | `crates/ecstore/src/set_disk/ops/locking.rs` candidate selection | Healing disks are ordered after new disks; scanning disks may remain candidates | Degraded/quorum fallback is preserved |
| Data movement | Existing storage-owned movement/publication generation (#1905/#1942) | This issue does not add a second coordinator | Future admission must validate the storage generation at the final commit |
## Baseline contract
The deterministic baseline in `scanner_heal_admission_baseline.rs` encodes the investigation matrix only: ScannerRead+HealRead may overlap, HealWrite conflicts with scanner reads, DataMovementWrite conflicts with all work, and independent set identities remain concurrent. It does not claim that production currently enforces the matrix.
The production facts that must be measured before Phase 1 are scanner p99, heal p99, cursor/checkpoint delay, queue and pending-ledger depth, and starvation by set. The benchmark matrix must include restart recovery, degraded quorum/scanning-disk fallback, urgent replacement heal, and at least two independent sets.
The executable fixture uses a fixed eight-sample restart/degraded sequence so the baseline is reproducible without wall-clock noise: two sets each receive ScannerRead, HealRead, HealWrite and a follow-up ScannerRead. Its expected synthetic p99 is 420 microseconds, maximum modeled backlog is 2, two HealWrite samples are deferred, and the independent second set still services three reads. These are fixture values, not production SLO claims; production benchmark output must replace them with measured p99, backlog and per-set wait distributions.
The inventory test reads the current source files and asserts the named guards/fallback branches are still present (`start_scan`, pending-ledger admission, Heal queue/active checks, namespace `get_write_lock`, and scanning-disk re-append). A source rename or guard removal therefore fails the baseline instead of silently leaving stale documentation.
Commit-time generation-fencing, lease-expiry, and lock-order tests are intentionally deferred until a Phase-0 fixture demonstrates a stale write or an SLO violation; arithmetic-only placeholders would stay green if production paths regressed.
If a future fixture demonstrates stale destructive writes, the fix must extend the storage-owned generation/admission primitive and validate the token at the final metadata/format/delete commit. Cancellation or a local lease alone is not a fence.
-149
View File
@@ -1,149 +0,0 @@
# CI gate matrix
This file is the source of truth for which validation runs on each event, its
configured wall-clock budget, and whether it can block a merge. Test taxonomy,
naming, and nextest serialization rules remain in [README.md](README.md); e2e
membership and counts remain in
[e2e-suite-inventory.md](e2e-suite-inventory.md).
The distinction between **required** and **report-only** is load-bearing:
a failing job blocks a merge only when its exact check name is present in the
live `main` ruleset. A workflow name, a `merge_group` trigger, or a red PR check
does not make a job required by itself.
## Required merge checks
The live `main` ruleset (`6436880`) currently requires exactly these contexts:
| Required context | Producer | Validation |
|---|---|---|
| `CLA Check` | `.github/workflows/cla.yml` | Contributor agreement |
| `Quick Checks` | `.github/workflows/ci.yml` | Formatting and repository guard scripts |
| `Test and Lint` | `.github/workflows/ci.yml` | Clippy, workspace nextest excluding `e2e_test`, doctests, and migration proofs |
For pull requests limited to the paths excluded by the main CI workflow,
`.github/workflows/ci-docs-only.yml` reports `Quick Checks` and
`Test and Lint` under the same names. It runs the real quick checks and the
planning-document guard; it does not claim that Rust compilation or runtime
tests ran. Despite the workflow name, these paths also include selected deploy,
workflow, and lock files.
Verify the live rule rather than trusting this snapshot before changing merge
policy:
```bash
gh api repos/rustfs/rustfs/rulesets/6436880 \
--jq '.rules[] | select(.type == "required_status_checks") | .parameters'
```
The ruleset currently has `strict_required_status_checks_policy=false`.
`Continuous Integration` accepts `merge_group` events and runs `e2e-full` for
them, but `End-to-End Tests (full merge gate)` is not currently a required
context. Therefore the repository is prepared to test a merge-queue SHA, but
the workflow alone does not prove that every merge passed that lane.
## Pull request and merge matrix
Budgets below are job `timeout-minutes`, not typical runtimes. “Report-only”
means the result is visible and actionable but is not in the live required
context list.
| Event | Validation | Budget | Merge status | Reproduction |
|---|---|---:|---|---|
| PR, non-doc change | `Quick Checks` | 10 min | Required | `make pre-commit` (broader local umbrella) |
| PR, non-doc change | `Test and Lint` | 90 min | Required | `cargo nextest run --profile ci --all --exclude e2e_test` |
| PR, non-doc change | `Typos` | 10 min | Report-only | `typos` |
| PR, non-doc change | `ILM Integration (serial)` | 90 min | Report-only | Use the exact command in `.github/workflows/ci.yml` |
| PR, non-doc change | rio-v2 / swift / sftp test-and-lint variants | 90 min each | Report-only | `cargo nextest run` with the workflow's feature set |
| PR, non-doc change | `Build RustFS Debug Binary` | 30 min | Report-only; prerequisite for black-box lanes | `cargo build -p rustfs --bins` |
| PR, non-doc change | `io_uring Integration (real)` | 30 min | Report-only | `cargo test -p rustfs-ecstore --lib uring_ -- --test-threads=1 --nocapture` |
| PR, non-doc change | `End-to-End Tests` (`e2e-smoke` plus `s3s-e2e`) | 30 min | Report-only | `cargo nextest run --profile e2e-smoke -p e2e_test`; then `./scripts/e2e-run.sh ./target/debug/rustfs <data-dir>` |
| PR, non-doc change | `S3 Implemented Tests` | 60 min | Report-only | Build `rustfs`, then run `scripts/s3-tests/run.sh` with `DEPLOY_MODE=binary`, `TEST_MODE=single`, and `MAXFAIL=0` |
| PR, non-doc change | `S3 Lifecycle Behavior Tests` | 30 min | Report-only | Use the accelerated scanner environment in `.github/workflows/ci.yml` with `scripts/s3-tests/run.sh` |
| PR touching dependency or workflow inputs | Cargo Deny / Workflow Pin Report / Dependency Review | 20 / 5 / 30 min | Report-only | `cargo deny check`; `scripts/security/check_workflow_pins.sh` |
| PR touching architecture rules or architecture docs | `Architecture Migration Rules` | 10 min | Report-only | `scripts/check_architecture_migration_rules.sh` |
| PR touching Nix or workspace manifests | `Nix Build & Check` | 60 min | Report-only | `nix flake check` |
| PR limited to main-CI-excluded paths | companion `Quick Checks` and `Test and Lint` | 10 min each | Required | `git diff --check`; `make doc-paths-check` when documentation paths changed |
| `merge_group` | Standard CI plus `e2e-full` | 55 min for `e2e-full` | Standard required contexts only; `e2e-full` report-only | `cargo nextest run --profile e2e-full -p e2e_test` |
| Push to `main` | Standard CI plus `e2e-full` | 55 min for `e2e-full` | Post-merge detection | Same as `merge_group` |
| PR touching fuzz inputs or harness paths | Build plus five 60-second fuzz smoke targets | 60 min build; 30 min per target | Report-only | `MAX_TOTAL_TIME=60 ./scripts/fuzz/run.sh` |
| PR touching selected ecstore disk/format paths | `Rename Safety` on Windows | 60 min | Report-only | Run the four `cargo test -p rustfs-ecstore --lib <filter>` commands in `windows-filesystem.yml` on Windows |
The authoritative e2e filters live in `.config/nextest.toml`; extend a profile
instead of adding a second ad-hoc selector. Before a profile runs,
`scripts/check_test_wiring.py` compares its exact membership to the committed
digest so a silent test drop fails closed.
## Scheduled and manual validation
Scheduled lanes are independent fault domains. They do not block a pull
request, but their workflow-local gate can fail the run and scheduled failures
are routed to the shared failure-issue action. The scheduled-validation
watchdog and freshness workflow separately detect incomplete runs and missing
schedules.
| Cadence (UTC unless noted) | Workflow / validation | Budget | Verdict and artifacts | Reproduction |
|---|---|---:|---|---|
| Daily 02:17 | Fuzz: five nightly corpus targets | 60 min build; 60 min per target | Gate; corpus/crash artifacts, scheduled failure alert | `MAX_TOTAL_TIME=<seconds> ./scripts/fuzz/run.sh` |
| Daily 03:17 | MinIO interop (EC + SSE read parity) | 40 min | Gate; scheduled failure alert | Dispatch `minio-interop.yml` or follow its pinned Docker fixture steps |
| Daily 04:29 | Replication / cluster-fault / protocol e2e | 45 / 90 / 90 min | Three independent gates; JUnit, membership, and server logs | `cargo nextest run --profile e2e-repl-nightly -p e2e_test`; `--profile e2e-nightly`; `-j 1 --profile e2e-protocols` |
| Daily 06:31 | Warp performance A/B | 180 min | Regression budget gate; A/B summaries and server logs | `bash scripts/run_hotpath_warp_abba.sh --help` |
| Daily 00:07 Asia/Shanghai (16:07 UTC previous day) | Nightly GNU build and Vault lanes | 150 / 90 / 60 min | Build, live Vault, and HA failover gates | Use the commands and pinned Vault images in `nightly-gnu.yml` |
| Daily 03:23 | Security Audit | 20 / 5 min, plus 30 min on PR dependency review | Cargo Deny and workflow-pin gates; scheduled failure alert | `cargo deny check`; `scripts/security/check_workflow_pins.sh` |
| Daily 23:47 | Scheduled Validation Freshness | 10 min | Fails when a critical schedule was never created or is stale | Dispatch `scheduled-validation-freshness.yml` |
| Sunday 00:11 | Full `Continuous Integration` matrix | Per-job budgets above | Weekly variant coverage, including dormant rio-v2 binary/e2e lanes | Dispatch `ci.yml` |
| Sunday 01:13 | Seven-platform build matrix | 150 min per platform | Build/package integrity; scheduled failure alert | Dispatch `build.yml` with an exact platform set |
| Sunday 02:19 | Ceph s3-tests full sweep: single and real four-node, four shards each | 180 min per shard | Compatibility gate; report, JUnit, exact node IDs, and server logs | `scripts/s3-tests/run.sh` against an existing single or distributed target |
| Sunday 06:41 | Mint | 120 min | **Report-only by design**; per-suite PASS/FAIL/NA and raw `log.json` | Reproduce the pinned Docker sequence in `mint.yml` or dispatch it |
| Sunday 07:43 | Workspace line coverage | 120 min | Report-only trend; lcov and JSON retained 90 days | `make coverage` |
| Monthly, day 1 06:37 | Runner Hygiene | 15 min | Validates runner ephemerality; scheduled failure alert | Dispatch `runner-hygiene.yml` |
Manual `workflow_dispatch` exists for the scheduled workflows above. Manual
runs are debugging evidence and intentionally do not open scheduled-failure
issues. A manual performance run may explicitly allow a known regression; that
override must not be treated as an ordinary passing baseline.
## Release validation
Release validation is post-merge and tag-driven; it does not substitute for a
pull-request gate.
| Event | Validation | Budget | Result |
|---|---|---:|---|
| Push to `main` or weekly schedule | `Build and Release` platform matrix | 150 min per platform | Build artifacts for all selected targets; no release publication on a main push |
| Valid release or preview tag | `Build and Release` plus asset checks | 150 min per platform | Draft release, checksummed assets, and publish step |
| Successful non-preview release-tag build | Docker image build and image scan | 60 min build; 30 min scan | Multi-architecture images plus vulnerability report |
| Successful release-tag build | DEB/RPM packaging | 30 min per architecture | Packages and checksum files uploaded to the release |
| Successful non-preview release-tag build | Helm template test and package | 30 min build; 30 min publish | Versioned chart and repository index |
Use an exact preview tag for end-to-end release rehearsal. Manual dispatches
are backfill/debug paths and do not prove the automatic `workflow_run` chain.
## Evidence requirements
A green check is useful only when it proves the intended behavior ran:
- Record the exact commit SHA and run URL.
- Separate product failure from runner prerequisites, service readiness, and
cancellation. Repair the precondition, then rerun the exact workload.
- Preserve membership manifests, JUnit, raw compatibility logs, seeds, and
server logs where the workflow provides them.
- For a bug fix or a new fault checker, provide sensitivity evidence: the old
behavior or an intentional mutation must fail the new oracle, and the fixed
behavior must pass it.
- Never promote a report-only lane to required from one green run. Require at
least 14 days and 30 representative pull requests with at least 99% complete
execution, then update the ruleset and this table together.
## Change checklist
Update this file in the same pull request when any of these change:
- workflow triggers, job names, timeouts, or nextest profile ownership;
- required status contexts or strict/merge-queue policy;
- scheduled cadence, alert routing, artifact contract, or local reproduction;
- report-only versus gating semantics.
Do not copy per-module test counts here. Update
[e2e-suite-inventory.md](e2e-suite-inventory.md) and its enforced membership
digest instead.
+2 -1
View File
@@ -41,6 +41,7 @@
| copy_object_version_restore_test | 2 | |
| copy_source_invalid_date_test | 1 | ✅ |
| create_bucket_region_test | 2 | ✅ |
| data_usage_test | 2 | |
| degraded_read_eof_regression_test | 3 | |
| delete_marker_migration_semantics_test | 2 | ✅ |
| delete_object_no_content_length_test | 1 | |
@@ -99,4 +100,4 @@
| tls_hot_reload_test | 1 | ✅ |
| version_id_regression_test | 10 | ✅ |
**Total listed: 575 tests across 82 modules · PR smoke: 163 tests / 36 modules · merge/main full: 453 tests / 73 modules · nightly replication: 55 tests · nightly cluster faults: 28 tests / 7 modules · nightly protocols: 16 tests** · updated 2026-08-23.
**Total listed: 577 tests across 83 modules · PR smoke: 163 tests / 36 modules · merge/main full: 455 tests / 74 modules · nightly replication: 55 tests · nightly cluster faults: 28 tests / 7 modules · nightly protocols: 16 tests** · updated 2026-08-23.