mirror of
https://github.com/partofmyid/register.git
synced 2026-06-05 18:46:50 +07:00
make generic
This commit is contained in:
parent
de22d7bd14
commit
f1c21760a5
2 changed files with 77 additions and 77 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -3,5 +3,5 @@ creds.json
|
||||||
types-dnscontrol.d.ts
|
types-dnscontrol.d.ts
|
||||||
|
|
||||||
# Zone files
|
# Zone files
|
||||||
result
|
result*
|
||||||
part-of.my.id.txt
|
part-of.my.id.txt
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# compare-zones.sh — Compare a Cloudflare zone export against a nix-built zone file
|
# compare-zones.sh — Compare two BIND-format zone files
|
||||||
|
#
|
||||||
|
# Normalizes both files (strips comments, TTLs, SOA records, and whitespace
|
||||||
|
# differences) then performs a record-by-record comparison.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./scripts/compare-zones.sh <cloudflare-export.txt> [nix-result]
|
# ./scripts/compare-zones.sh <zone-file-a> <zone-file-b>
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# cloudflare-export.txt Path to the Cloudflare zone export (BIND format)
|
|
||||||
# nix-result Path to the nix-built zone file (default: ./result)
|
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
# ./scripts/compare-zones.sh part-of.my.id.txt
|
# ./scripts/compare-zones.sh expected.zone generated.zone
|
||||||
# ./scripts/compare-zones.sh part-of.my.id.txt result
|
# ./scripts/compare-zones.sh part-of.my.id.txt result
|
||||||
# nix build .#0 && ./scripts/compare-zones.sh part-of.my.id.txt result
|
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
|
@ -24,56 +22,57 @@ BOLD='\033[1m'
|
||||||
RESET='\033[0m'
|
RESET='\033[0m'
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "Usage: $0 <cloudflare-export.txt> [nix-result]"
|
echo "Usage: $0 <zone-file-a> <zone-file-b>"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Compare a Cloudflare zone export against a nix-built zone file."
|
echo "Compare two BIND-format zone files."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Arguments:"
|
echo "Arguments:"
|
||||||
echo " cloudflare-export.txt Path to the Cloudflare BIND zone export"
|
echo " zone-file-a Path to the first zone file"
|
||||||
echo " nix-result Path to the nix-built zone file (default: ./result)"
|
echo " zone-file-b Path to the second zone file"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ $# -lt 1 ]]; then
|
if [[ $# -lt 2 ]]; then
|
||||||
usage
|
usage
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CF_EXPORT="$1"
|
FILE_A="$1"
|
||||||
NIX_RESULT="${2:-./result}"
|
FILE_B="$2"
|
||||||
|
|
||||||
if [[ ! -f "$CF_EXPORT" ]]; then
|
for f in "$FILE_A" "$FILE_B"; do
|
||||||
echo -e "${RED}Error:${RESET} Cloudflare export not found: $CF_EXPORT"
|
resolved="$f"
|
||||||
exit 1
|
# Resolve symlinks (e.g. nix store results)
|
||||||
fi
|
if [[ -L "$resolved" ]]; then
|
||||||
|
resolved="$(readlink -f "$resolved")"
|
||||||
|
fi
|
||||||
|
if [[ ! -f "$resolved" ]]; then
|
||||||
|
echo -e "${RED}Error:${RESET} File not found: $f"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
if [[ ! -e "$NIX_RESULT" ]]; then
|
# Resolve symlinks for display
|
||||||
echo -e "${RED}Error:${RESET} Nix result not found: $NIX_RESULT"
|
RESOLVED_A="$FILE_A"
|
||||||
echo "Hint: run 'nix build .#0' first"
|
RESOLVED_B="$FILE_B"
|
||||||
exit 1
|
[[ -L "$FILE_A" ]] && RESOLVED_A="$(readlink -f "$FILE_A")"
|
||||||
fi
|
[[ -L "$FILE_B" ]] && RESOLVED_B="$(readlink -f "$FILE_B")"
|
||||||
|
|
||||||
# If result is a symlink (nix build output), resolve it
|
|
||||||
if [[ -L "$NIX_RESULT" ]]; then
|
|
||||||
NIX_RESULT="$(readlink -f "$NIX_RESULT")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TMPDIR="$(mktemp -d)"
|
TMPDIR="$(mktemp -d)"
|
||||||
trap 'rm -rf "$TMPDIR"' EXIT
|
trap 'rm -rf "$TMPDIR"' EXIT
|
||||||
|
|
||||||
# normalize_zone <input-file> <output-file>
|
# normalize_zone <input-file> <output-file>
|
||||||
#
|
#
|
||||||
# Extracts resource records (A, AAAA, CNAME, MX, TXT, SRV, CAA, NS, SOA),
|
# Extracts resource records, strips comments, normalizes whitespace and TTLs,
|
||||||
# strips comments, normalizes whitespace and TTLs, and sorts.
|
# ensures FQDNs have trailing dots, skips SOA (which always differs), and sorts.
|
||||||
normalize_zone() {
|
normalize_zone() {
|
||||||
local input="$1"
|
local input="$1"
|
||||||
local output="$2"
|
local output="$2"
|
||||||
|
|
||||||
# 1. Remove comment-only lines and blank lines
|
# Resolve symlinks
|
||||||
# 2. Strip inline comments ("; ...")
|
if [[ -L "$input" ]]; then
|
||||||
# 3. Collapse whitespace
|
input="$(readlink -f "$input")"
|
||||||
# 4. Normalize: <name> <class> <type> <rdata> (drop TTL)
|
fi
|
||||||
# 5. Ensure FQDNs on NS/CNAME/MX targets end with a dot
|
|
||||||
# 6. Sort for stable comparison
|
|
||||||
grep -E '^\S+' "$input" \
|
grep -E '^\S+' "$input" \
|
||||||
| grep -v '^\s*;' \
|
| grep -v '^\s*;' \
|
||||||
| grep -v '^\s*$' \
|
| grep -v '^\s*$' \
|
||||||
|
|
@ -86,7 +85,7 @@ normalize_zone() {
|
||||||
# name TTL IN TYPE rdata...
|
# name TTL IN TYPE rdata...
|
||||||
# name IN TYPE rdata...
|
# name IN TYPE rdata...
|
||||||
#
|
#
|
||||||
# We want to output: name TYPE rdata...
|
# We output: name TYPE rdata...
|
||||||
|
|
||||||
name = $1
|
name = $1
|
||||||
idx = 2
|
idx = 2
|
||||||
|
|
@ -100,13 +99,13 @@ normalize_zone() {
|
||||||
rtype = toupper($idx)
|
rtype = toupper($idx)
|
||||||
idx++
|
idx++
|
||||||
|
|
||||||
# Skip SOA — it will always differ (serial, timers)
|
# Skip SOA — serial and timers will always differ
|
||||||
if (rtype == "SOA") next
|
if (rtype == "SOA") next
|
||||||
|
|
||||||
rdata = ""
|
rdata = ""
|
||||||
for (i = idx; i <= NF; i++) {
|
for (i = idx; i <= NF; i++) {
|
||||||
val = $i
|
val = $i
|
||||||
# Ensure trailing dot on targets for NS, CNAME, MX (last field)
|
# Ensure trailing dot on targets for NS, CNAME, MX
|
||||||
if ((rtype == "NS" || rtype == "CNAME") && i == idx) {
|
if ((rtype == "NS" || rtype == "CNAME") && i == idx) {
|
||||||
if (val !~ /\.$/) val = val "."
|
if (val !~ /\.$/) val = val "."
|
||||||
}
|
}
|
||||||
|
|
@ -123,76 +122,77 @@ normalize_zone() {
|
||||||
| sort > "$output"
|
| sort > "$output"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LABEL_A="$(basename "$FILE_A")"
|
||||||
|
LABEL_B="$(basename "$FILE_B")"
|
||||||
|
|
||||||
echo -e "${BOLD}Comparing zones${RESET}"
|
echo -e "${BOLD}Comparing zones${RESET}"
|
||||||
echo -e " Cloudflare export: ${CYAN}$CF_EXPORT${RESET}"
|
echo -e " A: ${CYAN}${RESOLVED_A}${RESET}"
|
||||||
echo -e " Nix result: ${CYAN}$NIX_RESULT${RESET}"
|
echo -e " B: ${CYAN}${RESOLVED_B}${RESET}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
normalize_zone "$CF_EXPORT" "$TMPDIR/cf.norm"
|
normalize_zone "$FILE_A" "$TMPDIR/a.norm"
|
||||||
normalize_zone "$NIX_RESULT" "$TMPDIR/nix.norm"
|
normalize_zone "$FILE_B" "$TMPDIR/b.norm"
|
||||||
|
|
||||||
CF_COUNT=$(wc -l < "$TMPDIR/cf.norm")
|
COUNT_A=$(wc -l < "$TMPDIR/a.norm")
|
||||||
NIX_COUNT=$(wc -l < "$TMPDIR/nix.norm")
|
COUNT_B=$(wc -l < "$TMPDIR/b.norm")
|
||||||
|
|
||||||
echo -e " Cloudflare records: ${BOLD}$CF_COUNT${RESET} (excluding SOA)"
|
echo -e " A records: ${BOLD}$COUNT_A${RESET} (excluding SOA)"
|
||||||
echo -e " Nix records: ${BOLD}$NIX_COUNT${RESET} (excluding SOA)"
|
echo -e " B records: ${BOLD}$COUNT_B${RESET} (excluding SOA)"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Compute differences
|
# Compute differences
|
||||||
# Lines only in Cloudflare = missing from nix
|
comm -23 "$TMPDIR/a.norm" "$TMPDIR/b.norm" > "$TMPDIR/only-a.txt"
|
||||||
# Lines only in nix = extra in nix
|
comm -13 "$TMPDIR/a.norm" "$TMPDIR/b.norm" > "$TMPDIR/only-b.txt"
|
||||||
comm -23 "$TMPDIR/cf.norm" "$TMPDIR/nix.norm" > "$TMPDIR/only-cf.txt"
|
comm -12 "$TMPDIR/a.norm" "$TMPDIR/b.norm" > "$TMPDIR/matching.txt"
|
||||||
comm -13 "$TMPDIR/cf.norm" "$TMPDIR/nix.norm" > "$TMPDIR/only-nix.txt"
|
|
||||||
comm -12 "$TMPDIR/cf.norm" "$TMPDIR/nix.norm" > "$TMPDIR/matching.txt"
|
|
||||||
|
|
||||||
MATCH_COUNT=$(wc -l < "$TMPDIR/matching.txt")
|
MATCH_COUNT=$(wc -l < "$TMPDIR/matching.txt")
|
||||||
ONLY_CF_COUNT=$(wc -l < "$TMPDIR/only-cf.txt")
|
ONLY_A_COUNT=$(wc -l < "$TMPDIR/only-a.txt")
|
||||||
ONLY_NIX_COUNT=$(wc -l < "$TMPDIR/only-nix.txt")
|
ONLY_B_COUNT=$(wc -l < "$TMPDIR/only-b.txt")
|
||||||
|
|
||||||
echo -e "${BOLD}Results${RESET}"
|
echo -e "${BOLD}Results${RESET}"
|
||||||
echo -e " ${GREEN}✓ Matching:${RESET} $MATCH_COUNT"
|
echo -e " ${GREEN}✓ Matching:${RESET} $MATCH_COUNT"
|
||||||
echo -e " ${RED}✗ Only in Cloudflare:${RESET} $ONLY_CF_COUNT (missing from nix build)"
|
echo -e " ${RED}✗ Only in A:${RESET} $ONLY_A_COUNT"
|
||||||
echo -e " ${YELLOW}+ Only in Nix:${RESET} $ONLY_NIX_COUNT (extra in nix build)"
|
echo -e " ${YELLOW}+ Only in B:${RESET} $ONLY_B_COUNT"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [[ "$ONLY_CF_COUNT" -gt 0 ]]; then
|
if [[ "$ONLY_A_COUNT" -gt 0 ]]; then
|
||||||
echo -e "${RED}${BOLD}Records only in Cloudflare (missing from nix):${RESET}"
|
echo -e "${RED}${BOLD}Records only in A (${LABEL_A}):${RESET}"
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
echo -e " ${RED}-${RESET} $line"
|
echo -e " ${RED}-${RESET} $line"
|
||||||
done < "$TMPDIR/only-cf.txt"
|
done < "$TMPDIR/only-a.txt"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$ONLY_NIX_COUNT" -gt 0 ]]; then
|
if [[ "$ONLY_B_COUNT" -gt 0 ]]; then
|
||||||
echo -e "${YELLOW}${BOLD}Records only in Nix (not in Cloudflare):${RESET}"
|
echo -e "${YELLOW}${BOLD}Records only in B (${LABEL_B}):${RESET}"
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
echo -e " ${YELLOW}+${RESET} $line"
|
echo -e " ${YELLOW}+${RESET} $line"
|
||||||
done < "$TMPDIR/only-nix.txt"
|
done < "$TMPDIR/only-b.txt"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$ONLY_CF_COUNT" -eq 0 && "$ONLY_NIX_COUNT" -eq 0 ]]; then
|
if [[ "$ONLY_A_COUNT" -eq 0 && "$ONLY_B_COUNT" -eq 0 ]]; then
|
||||||
echo -e "${GREEN}${BOLD}✓ Zones are identical!${RESET}"
|
echo -e "${GREEN}${BOLD}✓ Zones are identical!${RESET}"
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
# Show a unified-style diff for a quick overview
|
# Unified diff
|
||||||
echo -e "${BOLD}Diff (unified):${RESET}"
|
echo -e "${BOLD}Diff (unified):${RESET}"
|
||||||
diff -u \
|
diff -u \
|
||||||
--label "cloudflare" "$TMPDIR/cf.norm" \
|
--label "$LABEL_A" "$TMPDIR/a.norm" \
|
||||||
--label "nix" "$TMPDIR/nix.norm" \
|
--label "$LABEL_B" "$TMPDIR/b.norm" \
|
||||||
| head -80 || true
|
| head -80 || true
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Summarize by record type
|
# Summary by record type
|
||||||
echo -e "${BOLD}Summary by record type:${RESET}"
|
echo -e "${BOLD}Summary by record type:${RESET}"
|
||||||
echo -e " ${BOLD}Type CF-only Nix-only Matching${RESET}"
|
echo -e " ${BOLD}Type A-only B-only Matching${RESET}"
|
||||||
{
|
{
|
||||||
cat "$TMPDIR/only-cf.txt" "$TMPDIR/only-nix.txt" "$TMPDIR/matching.txt"
|
cat "$TMPDIR/only-a.txt" "$TMPDIR/only-b.txt" "$TMPDIR/matching.txt"
|
||||||
} | awk '{print $2}' | sort -u | while read -r rtype; do
|
} | awk '{print $2}' | sort -u | while read -r rtype; do
|
||||||
cf_only=$(grep -c "^[^ ]* ${rtype} " "$TMPDIR/only-cf.txt" || true)
|
a_only=$(grep -c "^[^ ]* ${rtype} " "$TMPDIR/only-a.txt" || true)
|
||||||
nix_only=$(grep -c "^[^ ]* ${rtype} " "$TMPDIR/only-nix.txt" || true)
|
b_only=$(grep -c "^[^ ]* ${rtype} " "$TMPDIR/only-b.txt" || true)
|
||||||
matching=$(grep -c "^[^ ]* ${rtype} " "$TMPDIR/matching.txt" || true)
|
matching=$(grep -c "^[^ ]* ${rtype} " "$TMPDIR/matching.txt" || true)
|
||||||
printf " %-6s %7d %8d %8d\n" "$rtype" "$cf_only" "$nix_only" "$matching"
|
printf " %-6s %6d %6d %8d\n" "$rtype" "$a_only" "$b_only" "$matching"
|
||||||
done
|
done
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue