diff options
| author | Fuwn <[email protected]> | 2026-02-26 17:18:01 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-26 17:18:01 -0800 |
| commit | f893796ef6aadcf0796d64e3afe3e2e72eab05d9 (patch) | |
| tree | 992dbd926b06c6f4c07c6be37999aa6fff9a85de | |
| parent | fix: use non-prefixed semver docker image tag (diff) | |
| download | plutia-test-f893796ef6aadcf0796d64e3afe3e2e72eab05d9.tar.xz plutia-test-f893796ef6aadcf0796d64e3afe3e2e72eab05d9.zip | |
chore: replace Makefile workflow with Taskfile tasks
| -rw-r--r-- | Makefile | 16 | ||||
| -rw-r--r-- | README.md | 21 | ||||
| -rw-r--r-- | Taskfile.yaml | 340 |
3 files changed, 352 insertions, 25 deletions
diff --git a/Makefile b/Makefile deleted file mode 100644 index f71992c..0000000 --- a/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -VERSION ?= $(shell cat VERSION 2>/dev/null || echo dev) -COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) -BUILD_DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) - -LDFLAGS := -s -w \ - -X main.version=$(VERSION) \ - -X main.commit=$(COMMIT) \ - -X main.buildDate=$(BUILD_DATE) - -.PHONY: build test - -build: - go build -trimpath -ldflags "$(LDFLAGS)" -o ./bin/plutia ./cmd/plutia - -test: - go test ./... @@ -28,9 +28,9 @@ Plutia is a deterministic, verifiable PLC mirror for `plc.directory` with signed ## Quick Start ```bash -go test ./... -go build ./cmd/plutia -./plutia serve --config=config.default.yaml +task build +task verify:small +task bench ``` ### CLI Commands @@ -64,7 +64,11 @@ go build -trimpath \ -o ./bin/plutia ./cmd/plutia ``` -`make build` provides the same pattern. +Task runner equivalent: + +```bash +task build +``` ## HTTP API @@ -155,14 +159,13 @@ See [`config.default.yaml`](./config.default.yaml). Core knobs: ```yaml services: plutia: - image: golang:1.25 - working_dir: /app - command: sh -lc "go build -trimpath -o /app/bin/plutia ./cmd/plutia && /app/bin/plutia serve --config=/app/config.default.yaml" + image: ghcr.io/fuwn/plutia:0.1.0 + command: ["plutia", "serve", "--config=/etc/plutia/config.yaml"] ports: - "8080:8080" volumes: - - ./:/app - - ./data:/app/data + - ./config.default.yaml:/etc/plutia/config.yaml:ro + - ./data:/var/lib/plutia restart: unless-stopped ``` diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 0000000..9bdc355 --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,340 @@ +version: "3" + +vars: + BIN: ./bin/plutia + VERSION: + sh: | + if [ ! -f VERSION ]; then + echo "VERSION file is required" >&2 + exit 1 + fi + tr -d '\r\n' < VERSION + VERSION_TAG: + sh: | + if [ ! -f VERSION ]; then + echo "VERSION file is required" >&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 "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 but was not found in PATH + - 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 not installed; skipping" + 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 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 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 the Plutia container image with the current semantic version. + preconditions: + - sh: command -v docker >/dev/null 2>&1 + msg: docker CLI is required for docker:build + cmds: + - docker build -t ghcr.io/fuwn/plutia:{{.VERSION_TAG}} . + + 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 |