Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions rhodium-standard-repositories/rsr-audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@ log_section() {

check() {
local description="$1"
local command="$2"
shift

TOTAL_CHECKS=$((TOTAL_CHECKS + 1))

if eval "$command" > /dev/null 2>&1; then
if "$@" > /dev/null 2>&1; then
PASSED_CHECKS=$((PASSED_CHECKS + 1))
log_success "$description"
return 0
Expand All @@ -131,6 +131,10 @@ check() {
fi
}

# not CMD [ARGS...] — negates a command's exit status (eval-free predicate,
# mirrors `not_contains` in scripts/propagate-workflow-pins.sh's test suite).
not() { ! "$@"; }

check_file_exists() {
local file="$1"
local description="${2:-File exists: $file}"
Expand Down Expand Up @@ -159,7 +163,7 @@ check_file_exists() {
check_dir_exists() {
local dir="$1"
local description="${2:-Directory exists: $dir}"
check "$description" "test -d '$REPO_PATH/$dir'"
check "$description" test -d "$REPO_PATH/$dir"
}

check_file_contains() {
Expand Down Expand Up @@ -190,7 +194,7 @@ check_file_contains() {
check_command_exists() {
local cmd="$1"
local description="${2:-Command available: $cmd}"
check "$description" "command -v $cmd"
check "$description" command -v "$cmd"
}

# =============================================================================
Expand All @@ -212,11 +216,14 @@ audit_category_1_infrastructure() {
check_file_contains "justfile" "validate" "Justfile has validate recipe"

# CI/CD: GitLab CI or GitHub Actions (the estate runs on GitHub; both count)
check "CI/CD configuration present" "test -f '$REPO_PATH/.gitlab-ci.yml' || ls '$REPO_PATH'/.github/workflows/*.y*ml >/dev/null 2>&1"
has_ci_config() {
[[ -f "$REPO_PATH/.gitlab-ci.yml" ]] || ls "$REPO_PATH"/.github/workflows/*.y*ml >/dev/null 2>&1
}
check "CI/CD configuration present" has_ci_config
if [[ -f "$REPO_PATH/.gitlab-ci.yml" ]]; then
check_file_contains ".gitlab-ci.yml" "stages:" "GitLab CI has stages defined"
else
check "CI/CD has workflows defined" "ls '$REPO_PATH'/.github/workflows/*.y*ml >/dev/null 2>&1"
check "CI/CD has workflows defined" ls "$REPO_PATH"/.github/workflows/*.y*ml
fi

# Podman (optional for CLI tools, required for web services)
Expand Down Expand Up @@ -317,23 +324,23 @@ audit_category_3_security() {

# Type safety (language detection)
if [[ -f "$REPO_PATH/Cargo.toml" ]]; then
check "Type-safe language: Rust" "true"
check "Type-safe language: Rust" true
elif [[ -f "$REPO_PATH/mix.exs" ]]; then
check "Type-safe language: Elixir" "true"
check "Type-safe language: Elixir" true
elif [[ -f "$REPO_PATH/package.json" ]]; then
check_file_contains "package.json" "rescript" "Type-safe: ReScript (not TypeScript)"
if grep -q "typescript" "$REPO_PATH/package.json" 2>/dev/null; then
log_warning "TypeScript detected (unsound gradual typing, prefer ReScript)"
fi
elif find "$REPO_PATH" -name "*.adb" -o -name "*.ada" | grep -q .; then
check "Type-safe language: Ada" "true"
check "Type-safe language: Ada" true
elif find "$REPO_PATH" -name "*.hs" | grep -q .; then
check "Type-safe language: Haskell" "true"
check "Type-safe language: Haskell" true
fi

# Memory safety
if [[ -f "$REPO_PATH/Cargo.toml" ]]; then
check "Memory-safe language: Rust" "true"
check "Memory-safe language: Rust" true

# Check for unsafe code blocks
local unsafe_count
Expand Down Expand Up @@ -369,7 +376,7 @@ audit_category_3_security() {

# Security headers configuration (for web projects)
if [[ -f "$REPO_PATH/nginx.conf" ]] || [[ -f "$REPO_PATH/apache.conf" ]] || grep -rq "Content-Security-Policy" "$REPO_PATH" 2>/dev/null; then
check "Security headers configured" "grep -rq 'Content-Security-Policy\\|X-Frame-Options\\|X-Content-Type-Options' '$REPO_PATH'"
check "Security headers configured" grep -rq 'Content-Security-Policy\|X-Frame-Options\|X-Content-Type-Options' "$REPO_PATH"
fi

# .well-known/security.txt validation (RFC 9116)
Expand All @@ -388,7 +395,7 @@ audit_category_4_architecture() {
log_section "Category 4: Architecture Principles"

# Offline-first indicators
check "Offline-first: No external API calls in core code" "! grep -rq 'http://\\|https://' '$REPO_PATH/src' 2>/dev/null"
check "Offline-first: No external API calls in core code" not grep -rq 'http://\|https://' "$REPO_PATH/src"

# CRDT usage (for distributed systems)
if grep -rq "CRDT\\|crdt\\|Conflict-free" "$REPO_PATH" 2>/dev/null; then
Expand All @@ -398,11 +405,11 @@ audit_category_4_architecture() {
TOTAL_CHECKS=$((TOTAL_CHECKS + 1))

# Reversibility (Git-based)
check "Reversibility: Git repository" "test -d '$REPO_PATH/.git'"
check "Reversibility: Git repository" test -d "$REPO_PATH/.git"

# Build reproducibility (Nix)
if [[ -f "$REPO_PATH/flake.nix" ]]; then
check "Reproducible builds: Nix flakes" "true"
check "Reproducible builds: Nix flakes" true
fi

# Documentation of architecture
Expand Down
35 changes: 20 additions & 15 deletions scripts/tests/fill-placeholders-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,31 @@ printf 'run *{{ARGS}}:\n echo {{ARGS}}\n' > Justfile
"$S" . --map map.json --apply >/dev/null

pass=0; fail=0
ck(){ if eval "$2"; then pass=$((pass+1)); echo " ok $1"; else fail=$((fail+1)); echo " FAIL $1"; fi; }
ck(){
local desc="$1"; shift
if "$@"; then pass=$((pass+1)); echo " ok $desc"
else fail=$((fail+1)); echo " FAIL $desc"
fi
}

ck "sed LHS placeholders survive (the 90-repo corruption)" \
'grep -q "s/{{PROJECT_NAME}}/" scripts/apply-common-files.sh'
grep -q "s/{{PROJECT_NAME}}/" scripts/apply-common-files.sh
ck "sed LHS {{DATE}} survives" \
'grep -q "s/{{DATE}}/" scripts/apply-common-files.sh'
grep -q "s/{{DATE}}/" scripts/apply-common-files.sh
ck "alternate sed delimiter | also protected" \
'grep -q "s|{{PROJECT_NAME}}|" scripts/apply-common-files.sh'
grep -q "s|{{PROJECT_NAME}}|" scripts/apply-common-files.sh
ck "ordinary prose IS substituted" \
'grep -q "Conative Gating readme" README.md'
grep -q "Conative Gating readme" README.md
ck "ordinary date IS substituted" \
'grep -q "Built on 2026-08-05" README.md'
grep -q "Built on 2026-08-05" README.md
ck "QUICKSTART left alone (its subject is the token)" \
'grep -q "Replace {{PROJECT_NAME}}, {{DEPS}}" QUICKSTART.adoc'
grep -q "Replace {{PROJECT_NAME}}, {{DEPS}}" QUICKSTART.adoc
ck "REQUIRES_INITIALISATION left alone" \
'grep -q -- "- {{PROJECT_NAME}}" REQUIRES_INITIALISATION.md'
grep -q -- "- {{PROJECT_NAME}}" REQUIRES_INITIALISATION.md
ck "template source keeps its tokens" \
'grep -q "{{PROJECT_NAME}}" templates/x.template'
grep -q "{{PROJECT_NAME}}" templates/x.template
ck "just's own {{ARGS}} never touched" \
'grep -qc "{{ARGS}}" Justfile'
grep -qc "{{ARGS}}" Justfile

# --- slug slots: a value can be correct AND wrong depending on where it lands.
# These reproduce findings from boj-server-mk2, project-ovine and squeakwell,
Expand All @@ -59,23 +64,23 @@ printf 'The {{PROJECT_NAME}} project builds things.\n' > README.md
"$S" . --map map.json --apply >/dev/null 2>&1 || true

ck "display name REFUSED in a Guix (name ...) slot" \
'grep -q "{{PROJECT_NAME}}" guix.scm'
grep -q "{{PROJECT_NAME}}" guix.scm
ck "display name still substituted in ordinary prose" \
'grep -q "The BoJ Server Mk2 project" README.md'
grep -q "The BoJ Server Mk2 project" README.md

printf '{"PROJECT_NAME":"BoJ Server Mk2","PROJECT_SLUG":"boj-server-mk2"}\n' > map.json
printf "(name '{{PROJECT_NAME}})\nurl \"https://github.com/x/{{PROJECT_NAME}}\"\n" > guix.scm
"$S" . --map map.json --apply >/dev/null 2>&1

ck "with PROJECT_SLUG supplied, slug slots get the SLUG" \
'grep -q "(name .boj-server-mk2)" guix.scm'
grep -q "(name .boj-server-mk2)" guix.scm
ck "and the URL gets the slug too" \
'grep -q "github.com/x/boj-server-mk2" guix.scm'
grep -q "github.com/x/boj-server-mk2" guix.scm

# just's own interpolations must never be touched, mapped or not
printf 'build -t {{project}}:latest\nrun *{{ARGS}}:\n' > Justfile
"$S" . --map map.json --apply >/dev/null 2>&1
ck "just {{project}} interpolation survives" 'grep -q "{{project}}:latest" Justfile'
ck "just {{project}} interpolation survives" grep -q "{{project}}:latest" Justfile

echo; echo " ${pass} passed, ${fail} failed"
[ "$fail" = "0" ]
Loading