Skip to content
Closed
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
70 changes: 70 additions & 0 deletions .github/workflows/validate-scrolls.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Validate Scrolls

on:
pull_request:
paths:
- 'scrolls/**/*.yaml'
- 'scrolls/**/*.lua'
- '.github/workflows/validate-scrolls.yml'
- 'scripts/validate-scrolls.sh'
push:
branches:
- master
paths:
- 'scrolls/**/*.yaml'
- 'scrolls/**/*.lua'
- '.github/workflows/validate-scrolls.yml'
- 'scripts/validate-scrolls.sh'

jobs:
validate:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run scroll validation
run: |
bash scripts/validate-scrolls.sh

- name: Upload validation results
if: always()
uses: actions/upload-artifact@v4
with:
name: validation-results
path: /tmp/scroll-validation-results.txt
retention-days: 30

- name: Comment PR with results (on failure)
if: failure() && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const resultsPath = '/tmp/scroll-validation-results.txt';

if (!fs.existsSync(resultsPath)) {
console.log('Results file not found');
return;
}

const results = fs.readFileSync(resultsPath, 'utf8');
const failures = results.split('\n')
.filter(line => line.startsWith('FAIL:'))
.slice(0, 20) // Limit to first 20 failures
.join('\n');

const body = `## ❌ Scroll Validation Failed

${failures}

See full results in the workflow artifacts.
`;

github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
72 changes: 72 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Scroll Validation Scripts

This directory contains validation scripts for the Druid scrolls repository.

## validate-scrolls.sh

Validates all `scroll.yaml` files in the repository.

### What it checks

- ✅ Required fields (`name`, `desc`, `app_version`)
- ✅ Port definitions (valid numbers, 1-65535 range)
- ✅ Sleep handler file existence
- ✅ Init command definition
- ✅ Commands section structure
- ✅ Procedures definition

### Usage

```bash
# From repository root
./scripts/validate-scrolls.sh

# Results are written to /tmp/scroll-validation-results.txt
```

### Exit codes

- `0` - All scrolls valid
- `1` - One or more scrolls failed validation

### CI Integration

This script is automatically run by GitHub Actions on:
- Pull requests that modify scroll files
- Pushes to master branch

See `.github/workflows/validate-scrolls.yml` for the workflow configuration.

## Common validation errors

### Missing sleep handler file

```
ERROR: Sleep handler not found: generic
```

**Fix:** Create the missing sleep handler file (e.g., `generic.lua`) or update the scroll to reference an existing handler.

### Invalid port number

```
ERROR: Port number out of range: 99999
```

**Fix:** Port numbers must be between 1 and 65535.

### Missing required field

```
ERROR: Missing required field: app_version
```

**Fix:** Add the missing field to the `scroll.yaml` file.

## Warnings (non-blocking)

Warnings don't fail the validation but should be reviewed:

- `No mandatory ports defined` - Consider adding `mandatory: true` to at least one port
- `No ports defined` - Scroll has no network ports (intentional for some utility scrolls)
- `No init command defined` - Scroll may not start properly
186 changes: 186 additions & 0 deletions scripts/validate-scrolls.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/bin/bash
# Scroll Validation Script - CI Version
# Validates all scroll.yaml files in the repository

set -euo pipefail

# Get repository root directory
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SCROLLS_DIR="$REPO_ROOT/scrolls"
RESULTS_FILE="/tmp/scroll-validation-results.txt"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

# Initialize counters
total=0
passed=0
failed=0

# Initialize results file
echo "Scroll Validation Results - $(date)" > "$RESULTS_FILE"
echo "======================================" >> "$RESULTS_FILE"
echo "" >> "$RESULTS_FILE"

# Function to validate a single scroll.yaml
validate_scroll() {
local scroll_file="$1"
local scroll_dir=$(dirname "$scroll_file")
local rel_path=${scroll_file#$SCROLLS_DIR/}
local display_name=$(dirname "$rel_path")

local errors=()
local warnings=()

# Check if file is readable
if [ ! -r "$scroll_file" ]; then
errors+=("File not readable")
fi

# Check required fields
if ! grep -q "^name:" "$scroll_file"; then
errors+=("Missing required field: name")
fi

if ! grep -q "^desc:" "$scroll_file"; then
errors+=("Missing required field: desc")
fi

if ! grep -q "^app_version:" "$scroll_file"; then
errors+=("Missing required field: app_version")
fi

# Check ports section
if ! grep -q "^ports:" "$scroll_file"; then
warnings+=("No ports defined")
else
# Check for mandatory ports
if ! grep -q "mandatory: true" "$scroll_file"; then
warnings+=("No mandatory ports defined")
fi

# Check port definitions (look for port: followed by a number)
while IFS= read -r line; do
if echo "$line" | grep -q "^ port: "; then
port_num=$(echo "$line" | sed 's/.*port: //' | tr -d ' ')
if ! [[ "$port_num" =~ ^[0-9]+$ ]]; then
errors+=("Invalid port number: $port_num")
elif [ "$port_num" -lt 1 ] || [ "$port_num" -gt 65535 ]; then
errors+=("Port number out of range: $port_num")
fi
fi
done < "$scroll_file"

# Check for sleep_handler files
while IFS= read -r line; do
if echo "$line" | grep -q "sleep_handler:"; then
handler=$(echo "$line" | sed 's/.*sleep_handler: //' | tr -d ' ')
handler_path="$scroll_dir/$handler"
if [ ! -f "$handler_path" ]; then
errors+=("Sleep handler not found: $handler")
fi
fi
done < "$scroll_file"
fi

# Check init field (should reference a command)
if ! grep -q "^init:" "$scroll_file"; then
warnings+=("No init command defined")
fi

# Check commands section
if grep -q "^commands:" "$scroll_file"; then
# Verify at least some commands exist
if ! grep -A 5 "^commands:" "$scroll_file" | grep -q " [a-z]"; then
warnings+=("Commands section empty")
fi

# Check for procedures in commands (basic check)
if ! grep -q "procedures:" "$scroll_file"; then
warnings+=("No procedures defined in commands")
fi
else
warnings+=("No commands section defined")
fi

# Check dependencies
if grep -q "^dependencies:" "$scroll_file"; then
# Just check if it's present, detailed validation would require YAML parsing
:
fi

# Report results
if [ ${#errors[@]} -eq 0 ]; then
echo -e "${GREEN}✓${NC} $display_name"
echo "PASS: $display_name" >> "$RESULTS_FILE"

# Show warnings
for warning in "${warnings[@]}"; do
echo -e " ${YELLOW}⚠${NC} $warning"
echo " WARNING: $warning" >> "$RESULTS_FILE"
done

return 0
else
echo -e "${RED}✗${NC} $display_name"
echo "FAIL: $display_name" >> "$RESULTS_FILE"

for error in "${errors[@]}"; do
echo -e " ${RED}→${NC} $error"
echo " ERROR: $error" >> "$RESULTS_FILE"
done

return 1
fi
}

# Main execution
echo "Scroll Validation Script"
echo "Repository: $REPO_ROOT"
echo "Scrolls directory: $SCROLLS_DIR"
echo "======================================"
echo

if [ ! -d "$SCROLLS_DIR" ]; then
echo -e "${RED}Error: Scrolls directory not found: $SCROLLS_DIR${NC}"
exit 1
fi

# Find and validate all scroll.yaml files
while IFS= read -r scroll_file; do
total=$((total + 1))

if validate_scroll "$scroll_file"; then
passed=$((passed + 1))
else
failed=$((failed + 1))
fi

done < <(find "$SCROLLS_DIR" -name "scroll.yaml" -type f | sort)

# Summary
echo
echo "======================================"
echo >> "$RESULTS_FILE"
echo "======================================" >> "$RESULTS_FILE"
echo "SUMMARY" >> "$RESULTS_FILE"
echo "======================================" >> "$RESULTS_FILE"
echo "Total: $total | Passed: $passed | Failed: $failed" >> "$RESULTS_FILE"

echo -e "${GREEN}✓ Passed:${NC} $passed"
echo -e "${RED}✗ Failed:${NC} $failed"
echo -e "Total: $total"
echo "======================================"
echo

if [ $failed -gt 0 ]; then
echo -e "${RED}Validation failed with $failed errors${NC}"
echo "See results file: $RESULTS_FILE"
exit 1
else
echo -e "${GREEN}All scrolls validated successfully!${NC}"
exit 0
fi
11 changes: 11 additions & 0 deletions scrolls/generic.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- Generic Sleep Handler
-- Simple handler that accepts any TCP connection and triggers server wake-up
-- Used by game servers without specific packet handlers

function handle(ctx, data)
debug_print("Generic sleep handler: received connection")
debug_print("Data length: " .. #data .. " bytes")

-- Trigger server wake-up
finish()
end
11 changes: 11 additions & 0 deletions scrolls/lgsm/arkserver/generic
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- Generic Sleep Handler
-- Simple handler that accepts any TCP connection and triggers server wake-up
-- Used by game servers without specific packet handlers

function handle(ctx, data)
debug_print("Generic sleep handler: received connection")
debug_print("Data length: " .. #data .. " bytes")

-- Trigger server wake-up
finish()
end
Loading