# Repetitive workflows. Anything a release or a review runs twice belongs here, # so a step cannot be forgotten by hand (v0.0.7 first failed on an unbumped # build.zig.zon that only verify-dist caught, one CI round too late). repo := "mokhtar/nxdns" forge := "https://git.mial.net" # every recipe, described default: @just --list # unit suite test: zig build test # unit + integration suite itest: zig build test -Dintegration # admin: typecheck, tests, lint, formatting admin-check: cd admin && npx tsc -b && npx vitest run && npx oxlint && npx prettier --check . # regenerate the contract goldens from live responses goldens: zig build test -Dintegration -Dcontract-samples-out="{{justfile_directory()}}/admin/src/lib/contractSamples.gen.ts" # the binary with the real admin UI embedded (the default build embeds a placeholder) build: cd admin && npm run build zig build -Dadmin-dist=admin/dist # everything CI gates on, locally verify: test itest admin-check zig fmt --check src/ tools/ # cut a release: preflight, bump, push, wait for CI, signed tag, watch the run. # Requires: clean tree, master, CHANGELOG section for the version already written. release version: #!/usr/bin/env sh set -eu v="{{version}}" [ -z "$(git status --porcelain)" ] || { echo "release: tree is dirty"; exit 1; } [ "$(git branch --show-current)" = "master" ] || { echo "release: not on master"; exit 1; } grep -q "^## \[$v\]" CHANGELOG.md || { echo "release: CHANGELOG.md has no ## [$v] section"; exit 1; } git ls-remote --exit-code --tags origin "v$v" >/dev/null 2>&1 && { echo "release: tag v$v already on origin"; exit 1; } if ! grep -q "\.version = \"$v\"" build.zig.zon; then sed -i "s/\.version = \"[^\"]*\"/.version = \"$v\"/" build.zig.zon grep -q "\.version = \"$v\"" build.zig.zon || { echo "release: version bump failed"; exit 1; } git add build.zig.zon git commit -S -m "build: bump version to $v" fi git push origin master sha=$(git rev-parse HEAD) echo "release: waiting for CI on $sha" while :; do state=$(curl -sf "{{forge}}/api/v1/repos/{{repo}}/commits/$sha/status" | python3 -c "import json,sys; print(json.load(sys.stdin).get('state') or 'pending')") || state=pending case "$state" in success) break ;; pending) sleep 30 ;; *) echo "release: CI is $state on $sha — not tagging"; exit 1 ;; esac done git tag -s "v$v" -m "v$v" "$sha" git push origin "v$v" echo "release: tag v$v pushed, watching the release run" while :; do line=$(curl -sf "{{forge}}/api/v1/repos/{{repo}}/actions/runs?limit=10" | python3 -c " import json,sys for r in json.load(sys.stdin).get('workflow_runs', []): if r.get('path','').startswith('release.yml') and r.get('head_sha') == '$sha': print(r.get('status',''), r.get('conclusion','') or ''); break ") || line="" set -- $line "" if [ "${1:-}" = "completed" ]; then [ "$2" = "success" ] || { echo "release: workflow concluded $2 — read the publish job log"; exit 1; } break fi sleep 60 done curl -sf "{{forge}}/api/v1/repos/{{repo}}/releases/tags/v$v" | python3 -c " import json,sys d = json.load(sys.stdin) print('release: published', d['tag_name'], 'with', len(d.get('assets', [])), 'assets') for a in d.get('assets', []): print(' ', a['name']) "