first cycle lab release
This commit is contained in:
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
# Disposable runtime restored from Phase 5; primary server is never selected.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
evidence="$PWD/.tools/phase6-evidence"
|
||||
root="$PWD/.tools/phase6-server"
|
||||
loop_root="$PWD/.tools/phase6-loop-server"
|
||||
mkdir -p "$evidence"
|
||||
[[ ! -e "$root" && ! -e "$loop_root" ]] || { echo 'Phase 6 fixtures already exist; use new roots to repeat.' >&2; exit 1; }
|
||||
snapshot=$(cat .tools/phase5-evidence/snapshot-path.txt)
|
||||
./mcpack restore --backup "$snapshot" --destination "$root"
|
||||
export MC_ROOT="$root" MC_RELEASE_DIR="$root/release"
|
||||
cp .tools/phase4-server/.env "$root/.env"
|
||||
chmod 600 "$root/.env"
|
||||
# A real cold JVM start cannot finish within two seconds.
|
||||
if scripts/server.sh up --wait --wait-timeout 2 --stable-seconds 1 > "$evidence/timeout.txt" 2>&1; then
|
||||
echo 'Short startup timeout unexpectedly passed' >&2; exit 1
|
||||
fi
|
||||
scripts/server.sh health --wait-timeout 120 --stable-seconds 3 > "$evidence/healthy.txt" 2>&1
|
||||
scripts/server.sh logs --tail 100 > "$evidence/healthy-start.log"
|
||||
# Abrupt termination only affects this disposable copied world.
|
||||
docker kill --signal KILL minecraft-lab-phase4-minecraft-1 > "$evidence/kill.txt"
|
||||
if scripts/server.sh health --wait-timeout 30 --stable-seconds 2 > "$evidence/crash.txt" 2>&1; then
|
||||
echo 'Crashed container unexpectedly healthy' >&2; exit 1
|
||||
fi
|
||||
scripts/server.sh up --wait --wait-timeout 120 --stable-seconds 3 > "$evidence/recovered.txt" 2>&1
|
||||
scripts/server.sh down
|
||||
# Deliberately broken Compose fixture, separate from immutable public release.
|
||||
mkdir -p "$loop_root/release"
|
||||
cp -a "$MC_RELEASE_DIR/." "$loop_root/release/"
|
||||
cp "$root/.env" "$loop_root/.env"
|
||||
LOOP_ROOT="$loop_root" node --input-type=module <<'JS'
|
||||
import fs from 'node:fs';
|
||||
import crypto from 'node:crypto';
|
||||
import {parse,stringify} from 'yaml';
|
||||
const release=process.env.LOOP_ROOT+'/release';
|
||||
const composePath=release+'/server/compose.yaml';
|
||||
const compose=parse(fs.readFileSync(composePath,'utf8'));
|
||||
compose.services.minecraft.restart='on-failure:5';
|
||||
compose.services.minecraft.entrypoint=['/bin/sh','-c','echo phase6-intentional-restart-loop; sleep 1; exit 42'];
|
||||
fs.writeFileSync(composePath,stringify(compose));
|
||||
const hash=file=>crypto.createHash('sha256').update(fs.readFileSync(release+'/'+file)).digest('hex');
|
||||
const manifest=JSON.parse(fs.readFileSync(release+'/manifest.json'));
|
||||
manifest.pack.version='phase6-failure-fixture';
|
||||
for(const file of manifest.release_files)file.sha256=hash(file.path);
|
||||
fs.writeFileSync(release+'/manifest.json',JSON.stringify(manifest,null,2)+'\n');
|
||||
const files=[...manifest.release_files.map(file=>file.path),'manifest.json'].sort();
|
||||
fs.writeFileSync(release+'/SHA256SUMS',files.map(file=>`${hash(file)} ${file}\n`).join(''));
|
||||
JS
|
||||
export MC_ROOT="$loop_root" MC_RELEASE_DIR="$loop_root/release"
|
||||
scripts/server.sh up -d
|
||||
# Wait until the real Docker container has restarted at least once.
|
||||
for attempt in {1..20}; do
|
||||
count=$(docker inspect minecraft-lab-phase4-minecraft-1 --format '{{.RestartCount}}')
|
||||
if [[ "$count" -ge 1 ]]; then break; fi
|
||||
sleep 0.2
|
||||
done
|
||||
docker inspect minecraft-lab-phase4-minecraft-1 --format '{{json .State}} {{.RestartCount}}' > "$evidence/loop-docker-state.txt"
|
||||
if scripts/server.sh health --wait-timeout 15 --stable-seconds 2 > "$evidence/restart-loop.txt" 2>&1; then
|
||||
echo 'Restart loop unexpectedly healthy' >&2; exit 1
|
||||
fi
|
||||
scripts/server.sh down
|
||||
ROOT="$root" LOOP_ROOT="$loop_root" node --input-type=module <<'JS' > "$evidence/assertions.txt"
|
||||
import fs from 'node:fs';
|
||||
import assert from 'node:assert/strict';
|
||||
const reports=root=>fs.readdirSync(root+'/diagnostics').filter(name=>name.endsWith('.json')).map(name=>JSON.parse(fs.readFileSync(root+'/diagnostics/'+name)));
|
||||
const normal=reports(process.env.ROOT),loop=reports(process.env.LOOP_ROOT);
|
||||
assert(normal.some(report=>report.reason==='timeout'));
|
||||
assert(normal.some(report=>report.reason==='crash'&&report.state.exit_code===137));
|
||||
assert(loop.some(report=>report.reason==='restart-loop'));
|
||||
for(const root of [process.env.ROOT,process.env.LOOP_ROOT]){
|
||||
assert(!fs.existsSync(root+'/.mcpack-runtime.lock'));
|
||||
assert(fs.existsSync(root+'/.mcpack-runtime.json'));
|
||||
for(const report of reports(root)){
|
||||
assert(fs.existsSync(report.logs));
|
||||
assert.equal(report.recovery.automatic_rollback,false);
|
||||
assert.equal(report.recovery.runtime_preserved,true);
|
||||
}
|
||||
}
|
||||
assert(fs.existsSync(process.env.ROOT+'/runtime/mod-state/phase5.json'));
|
||||
console.log('Real success, timeout, crash (exit 137), restart-loop, diagnostics and runtime/binding preservation passed.');
|
||||
JS
|
||||
scripts/spike-server.sh ps > "$evidence/primary-server-status.txt"
|
||||
printf 'Phase 6 integration passed.\n'
|
||||
Reference in New Issue
Block a user