version: "3" vars: BIN: ./bin/plutia VERSION: sh: | if [ ! -f VERSION ]; then echo "VERSION file is required. Create VERSION and run the task again." >&2 exit 1 fi tr -d '\r\n' < VERSION VERSION_TAG: sh: | if [ ! -f VERSION ]; then echo "VERSION file is required. Create VERSION and run the task again." >&2 exit 1 fi v="$(tr -d '\r\n' < VERSION)" echo "${v#v}" COMMIT: sh: | if ! command -v git >/dev/null 2>&1; then echo "Git is required to compute build metadata." >&2 exit 1 fi if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo "A Git repository context is required." >&2 exit 1 fi git rev-parse --short HEAD BUILD_DATE: sh: date -u +%Y-%m-%dT%H:%M:%SZ LDFLAGS: -s -w -X main.version={{.VERSION}} -X main.commit={{.COMMIT}} -X main.buildDate={{.BUILD_DATE}} TEST_MIRROR_KEY_HEX: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef tasks: default: desc: Show available tasks. cmds: - task --list env:git: internal: true desc: Ensure git metadata is available for version injection. preconditions: - sh: command -v git >/dev/null 2>&1 msg: Git is required. Install Git and run the task again. - sh: git rev-parse --is-inside-work-tree >/dev/null 2>&1 msg: task must run from inside the plutia git repository cmds: - echo "git metadata ready" _reset-dir: internal: true requires: vars: [DIR] preconditions: - sh: command -v trash >/dev/null 2>&1 msg: "trash command is required for safe cleanup" cmds: - | if [ -e "{{.DIR}}" ]; then trash "{{.DIR}}" fi - mkdir -p "{{.DIR}}" _write-mirror-key: internal: true requires: vars: [DIR] cmds: - mkdir -p "{{.DIR}}" - printf "%s\n" "{{.TEST_MIRROR_KEY_HEX}}" > "{{.DIR}}/mirror.key" build: desc: Build plutia with version metadata and trimpath. deps: [env:git] cmds: - mkdir -p ./bin - go build -trimpath -ldflags '{{.LDFLAGS}}' -o {{.BIN}} ./cmd/plutia build:release: desc: Build a static release binary at ./bin/plutia. deps: [env:git] cmds: - mkdir -p ./bin - CGO_ENABLED=0 go build -trimpath -ldflags '{{.LDFLAGS}}' -o {{.BIN}} ./cmd/plutia clean: desc: Remove build outputs and runtime test data. cmds: - | for path in \ ./bin \ ./tmp-test-data \ ./tmp-test-data-resolver \ ./tmp-test-data-corruption \ ./scale-test-data \ ./scale-test-data-resolver \ ./scale-test-data-resolver-stateonly \ ./scale-test-data-1m \ ./bench-data-mirror \ ./bench-data-resolver; do if [ -e "$path" ]; then rm -rf "$path" fi done test: desc: Run all tests once with no cache. cmds: - go test ./... -count=1 test:race: desc: Run tests with the race detector. cmds: - go test -race ./... lint: desc: Run vet and staticcheck (if installed). cmds: - go vet ./... - | if command -v staticcheck >/dev/null 2>&1; then staticcheck ./... else echo "staticcheck is not installed, so this step is skipped." fi verify:small: desc: Mirror/full verification on first 10k ops with checkpoint assertion. deps: [build:release] env: PLUTIA_MODE: mirror PLUTIA_VERIFY: full PLUTIA_DATA_DIR: ./tmp-test-data PLUTIA_CHECKPOINT_INTERVAL: "1000" PLUTIA_BLOCK_SIZE_MB: "4" PLUTIA_ZSTD_LEVEL: "9" PLUTIA_MIRROR_PRIVATE_KEY_PATH: ./tmp-test-data/mirror.key cmds: - task: _reset-dir vars: { DIR: ./tmp-test-data } - task: _write-mirror-key vars: { DIR: ./tmp-test-data } - "{{.BIN}} replay --config=config.default.yaml --max-ops=10000 | tee ./tmp-test-data/verify-small.log" - | checkpoint_count="$(find ./tmp-test-data/checkpoints -maxdepth 1 -type f -name '*.json' ! -name '*.state.json' | wc -l | awk '{print $1}')" if [ "${checkpoint_count}" -lt 1 ]; then echo "verify:small failed: no checkpoint files were generated." >&2 exit 1 fi global_seq="$(awk '/replay complete at sequence/ {print $5}' ./tmp-test-data/verify-small.log | tail -n 1)" latest_state="$(ls -1 ./tmp-test-data/checkpoints/*.state.json 2>/dev/null | sort | tail -n 1)" did_count="0" if [ -n "${latest_state}" ]; then did_count="$(grep -o '"DID":"' "${latest_state}" | wc -l | awk '{print $1}')" fi echo "verify:small summary" echo " global_seq=${global_seq:-unknown}" echo " checkpoint_files=${checkpoint_count}" echo " did_count=${did_count}" echo " disk_total=$(du -sh ./tmp-test-data | awk '{print $1}')" verify:scale: desc: Mirror/full 200k verification with throughput and storage metrics. deps: [build:release] env: PLUTIA_MODE: mirror PLUTIA_VERIFY: full PLUTIA_DATA_DIR: ./scale-test-data PLUTIA_MIRROR_PRIVATE_KEY_PATH: ./scale-test-data/mirror.key cmds: - task: _reset-dir vars: { DIR: ./scale-test-data } - task: _write-mirror-key vars: { DIR: ./scale-test-data } - "{{.BIN}} bench --config=config.default.yaml --max-ops=200000 --interval=15s | tee ./scale-test-data/verify-scale.log" - | latest_state="$(ls -1 ./scale-test-data/checkpoints/*.state.json 2>/dev/null | sort | tail -n 1)" did_count="0" if [ -n "${latest_state}" ]; then did_count="$(grep -o '"DID":"' "${latest_state}" | wc -l | awk '{print $1}')" fi echo "verify:scale summary" grep 'bench_total' ./scale-test-data/verify-scale.log | tail -n 1 grep 'bench_disk' ./scale-test-data/verify-scale.log | tail -n 1 echo "did_count=${did_count}" echo "ops_dir=$(du -sh ./scale-test-data/ops | awk '{print $1}')" echo "index_dir=$(du -sh ./scale-test-data/index | awk '{print $1}')" echo "checkpoints_dir=$(du -sh ./scale-test-data/checkpoints | awk '{print $1}')" verify:1m: desc: Mirror/full 1M replay with 50k checkpoint interval and timing extraction. deps: [build:release] env: PLUTIA_MODE: mirror PLUTIA_VERIFY: full PLUTIA_DATA_DIR: ./scale-test-data-1m PLUTIA_CHECKPOINT_INTERVAL: "50000" PLUTIA_MIRROR_PRIVATE_KEY_PATH: ./scale-test-data-1m/mirror.key cmds: - task: _reset-dir vars: { DIR: ./scale-test-data-1m } - task: _write-mirror-key vars: { DIR: ./scale-test-data-1m } - "{{.BIN}} replay --config=config.default.yaml --max-ops=1000000 2>&1 | tee ./scale-test-data-1m/verify-1m.log" - | echo "verify:1m checkpoint timing metrics" if ! grep -q 'checkpoint_metrics' ./scale-test-data-1m/verify-1m.log; then echo "verify:1m failed: no checkpoint metrics were emitted." >&2 exit 1 fi grep 'checkpoint_metrics' ./scale-test-data-1m/verify-1m.log verify:resolver: desc: Resolver/full 200k replay with no block archive assertion. deps: [build:release] env: PLUTIA_MODE: resolver PLUTIA_VERIFY: full PLUTIA_DATA_DIR: ./scale-test-data-resolver PLUTIA_CHECKPOINT_INTERVAL: "1000000000" cmds: - task: _reset-dir vars: { DIR: ./scale-test-data-resolver } - "{{.BIN}} replay --config=config.default.yaml --max-ops=200000 | tee ./scale-test-data-resolver/verify-resolver.log" - | block_files="$(find ./scale-test-data-resolver/ops -maxdepth 1 -type f -name '*.zst' | wc -l | awk '{print $1}')" if [ "${block_files}" -ne 0 ]; then echo "verify:resolver failed: expected zero block files, found ${block_files}." >&2 exit 1 fi echo "verify:resolver summary" awk '/replay complete at sequence/ {print}' ./scale-test-data-resolver/verify-resolver.log | tail -n 1 echo "block_files=${block_files}" echo "ops_dir=$(du -sh ./scale-test-data-resolver/ops | awk '{print $1}')" echo "index_dir=$(du -sh ./scale-test-data-resolver/index | awk '{print $1}')" echo "checkpoints_dir=$(du -sh ./scale-test-data-resolver/checkpoints | awk '{print $1}')" echo "total_dir=$(du -sh ./scale-test-data-resolver | awk '{print $1}')" verify:corruption: desc: Validate corruption detection by tampering with a persisted block file. deps: [build:release] env: PLUTIA_MODE: mirror PLUTIA_VERIFY: full PLUTIA_DATA_DIR: ./tmp-test-data-corruption PLUTIA_CHECKPOINT_INTERVAL: "1000" PLUTIA_BLOCK_SIZE_MB: "4" PLUTIA_MIRROR_PRIVATE_KEY_PATH: ./tmp-test-data-corruption/mirror.key cmds: - task: _reset-dir vars: { DIR: ./tmp-test-data-corruption } - task: _write-mirror-key vars: { DIR: ./tmp-test-data-corruption } - "{{.BIN}} replay --config=config.default.yaml --max-ops=5000 > ./tmp-test-data-corruption/initial.log" - | block_file="$(find ./tmp-test-data-corruption/ops -maxdepth 1 -type f -name '*.zst' | sort | head -n 1)" if [ -z "${block_file}" ]; then echo "verify:corruption failed: no block file was created." >&2 exit 1 fi printf 'tamper' >> "${block_file}" - | if {{.BIN}} replay --config=config.default.yaml --max-ops=5000 > ./tmp-test-data-corruption/restart.log 2>&1; then echo "verify:corruption failed: replay succeeded after tampering." >&2 exit 1 fi if ! grep -qi 'corruption' ./tmp-test-data-corruption/restart.log; then echo "verify:corruption failed: corruption was not reported." >&2 cat ./tmp-test-data-corruption/restart.log exit 1 fi echo "verify:corruption passed (corruption detected)" bench: desc: Run the default benchmark (mirror/full, 200k ops). cmds: - task: bench:mirror bench:mirror: desc: Mirror/full benchmark at 200k ops. deps: [build:release] env: PLUTIA_MODE: mirror PLUTIA_VERIFY: full PLUTIA_DATA_DIR: ./bench-data-mirror PLUTIA_MIRROR_PRIVATE_KEY_PATH: ./bench-data-mirror/mirror.key cmds: - task: _reset-dir vars: { DIR: ./bench-data-mirror } - task: _write-mirror-key vars: { DIR: ./bench-data-mirror } - "{{.BIN}} bench --config=config.default.yaml --max-ops=200000 --interval=10s" bench:resolver: desc: Resolver/full benchmark at 200k ops. deps: [build:release] env: PLUTIA_MODE: resolver PLUTIA_VERIFY: full PLUTIA_DATA_DIR: ./bench-data-resolver PLUTIA_CHECKPOINT_INTERVAL: "1000000000" cmds: - task: _reset-dir vars: { DIR: ./bench-data-resolver } - "{{.BIN}} bench --config=config.default.yaml --max-ops=200000 --interval=10s" release:local: desc: Build a static binary, print sha256, and report version metadata. deps: [build:release] cmds: - | if command -v sha256sum >/dev/null 2>&1; then sha256sum {{.BIN}} elif command -v shasum >/dev/null 2>&1; then shasum -a 256 {{.BIN}} else echo "sha256 tool unavailable (need sha256sum or shasum)" >&2 exit 1 fi - "{{.BIN}} version" docker:build: desc: Build and load local Plutia container images (version + latest tags). preconditions: - sh: command -v docker >/dev/null 2>&1 msg: docker CLI is required for docker:build - sh: docker buildx version >/dev/null 2>&1 msg: docker buildx is required for docker:build cmds: - docker buildx build --build-arg VERSION={{.VERSION}} --build-arg COMMIT={{.COMMIT}} --build-arg BUILD_DATE={{.BUILD_DATE}} -t ghcr.io/fuwn/plutia:{{.VERSION_TAG}} -t ghcr.io/fuwn/plutia:latest --load . docker:push: desc: Build and push multi-arch Plutia images (version + latest tags). preconditions: - sh: command -v docker >/dev/null 2>&1 msg: docker CLI is required for docker:push - sh: docker buildx version >/dev/null 2>&1 msg: docker buildx is required for multi-architecture docker:push cmds: - docker buildx build --platform linux/amd64,linux/arm64 --build-arg VERSION={{.VERSION}} --build-arg COMMIT={{.COMMIT}} --build-arg BUILD_DATE={{.BUILD_DATE}} -t ghcr.io/fuwn/plutia:{{.VERSION_TAG}} -t ghcr.io/fuwn/plutia:latest --push . docker:run: desc: Run Plutia container with mounted config and persistent data. preconditions: - sh: command -v docker >/dev/null 2>&1 msg: docker CLI is required for docker:run cmds: - mkdir -p ./data - docker run --rm -p 8080:8080 -v "${PWD}/config.default.yaml:/etc/plutia/config.yaml:ro" -v "${PWD}/data:/var/lib/plutia" ghcr.io/fuwn/plutia:{{.VERSION_TAG}} plutia serve --config=/etc/plutia/config.yaml