mirror of
https://github.com/trustedsec/SysmonCommunityGuide/
synced 2026-06-06 19:46:57 +00:00
2f6a5c265c
update build to add new cover page .. painful
388 lines
9.5 KiB
Bash
Executable File
388 lines
9.5 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Enhanced Sysmon Guide Build Script
|
|
# Provides automated chapter assembly and PDF generation with validation
|
|
|
|
set -e # Exit on any error
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
BUILD_DIR="${SCRIPT_DIR}/Build"
|
|
CHAPTERS_CONFIG="${SCRIPT_DIR}/chapters.json"
|
|
MASTER_FILE="${BUILD_DIR}/Sysmon.md"
|
|
OUTPUT_PDF="SysmonGuide.pdf"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Logging functions
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Help function
|
|
show_help() {
|
|
cat << EOF
|
|
Sysmon Community Guide Build Script
|
|
|
|
USAGE:
|
|
$0 [OPTIONS] [COMMAND]
|
|
|
|
COMMANDS:
|
|
build Build master markdown document (default)
|
|
pdf Build master document and generate PDF
|
|
clean Clean build artifacts
|
|
validate Validate chapter files and configuration
|
|
docker Build using Docker container
|
|
help Show this help message
|
|
|
|
OPTIONS:
|
|
-c, --config FILE Use alternative chapters.json file
|
|
-o, --output FILE Output PDF filename (default: $OUTPUT_PDF)
|
|
-v, --verbose Enable verbose output
|
|
-q, --quiet Suppress non-error output
|
|
--no-validation Skip file validation
|
|
--force Force rebuild even if files haven't changed
|
|
|
|
EXAMPLES:
|
|
$0 # Build master document
|
|
$0 pdf # Build document and generate PDF
|
|
$0 --config custom.json # Use custom configuration
|
|
$0 pdf --output MyGuide.pdf # Generate PDF with custom name
|
|
$0 docker # Build using Docker
|
|
|
|
EOF
|
|
}
|
|
|
|
# Parse command line arguments
|
|
VERBOSE=false
|
|
QUIET=false
|
|
VALIDATE=true
|
|
FORCE=false
|
|
COMMAND="build"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-c|--config)
|
|
CHAPTERS_CONFIG="$2"
|
|
shift 2
|
|
;;
|
|
-o|--output)
|
|
OUTPUT_PDF="$2"
|
|
shift 2
|
|
;;
|
|
-v|--verbose)
|
|
VERBOSE=true
|
|
shift
|
|
;;
|
|
-q|--quiet)
|
|
QUIET=true
|
|
shift
|
|
;;
|
|
--no-validation)
|
|
VALIDATE=false
|
|
shift
|
|
;;
|
|
--force)
|
|
FORCE=true
|
|
shift
|
|
;;
|
|
build|pdf|clean|validate|docker|help)
|
|
COMMAND="$1"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
*)
|
|
log_error "Unknown option: $1"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Conditional logging based on quiet flag
|
|
log_cmd() {
|
|
if [[ "$QUIET" != "true" ]]; then
|
|
log_info "$1"
|
|
fi
|
|
}
|
|
|
|
log_verbose() {
|
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
log_info "$1"
|
|
fi
|
|
}
|
|
|
|
# Validation functions
|
|
check_dependencies() {
|
|
local missing_deps=()
|
|
|
|
if ! command -v python3 &> /dev/null; then
|
|
missing_deps+=("python3")
|
|
fi
|
|
|
|
if [[ "$COMMAND" == "pdf" ]]; then
|
|
if ! command -v pandoc &> /dev/null; then
|
|
missing_deps+=("pandoc")
|
|
fi
|
|
|
|
# Add MacTeX to PATH for dependency check
|
|
if [[ -d "/usr/local/texlive" ]]; then
|
|
TEXLIVE_BIN=$(find /usr/local/texlive -name "xelatex" 2>/dev/null | head -1)
|
|
if [[ -n "$TEXLIVE_BIN" ]]; then
|
|
export PATH="$(dirname "$TEXLIVE_BIN"):$PATH"
|
|
fi
|
|
fi
|
|
|
|
if ! command -v xelatex &> /dev/null; then
|
|
missing_deps+=("xelatex")
|
|
fi
|
|
fi
|
|
|
|
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
|
log_error "Missing required dependencies:"
|
|
for dep in "${missing_deps[@]}"; do
|
|
echo " - $dep"
|
|
done
|
|
log_info "Consider using Docker build: $0 docker"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
validate_config() {
|
|
if [[ ! -f "$CHAPTERS_CONFIG" ]]; then
|
|
log_error "Configuration file not found: $CHAPTERS_CONFIG"
|
|
exit 1
|
|
fi
|
|
|
|
if ! python3 -c "import json; json.load(open('$CHAPTERS_CONFIG'))" 2>/dev/null; then
|
|
log_error "Invalid JSON in configuration file: $CHAPTERS_CONFIG"
|
|
exit 1
|
|
fi
|
|
|
|
log_verbose "Configuration file validated: $CHAPTERS_CONFIG"
|
|
}
|
|
|
|
validate_chapters() {
|
|
log_cmd "Validating chapter files..."
|
|
|
|
if ! python3 -c "
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
config = json.load(open('$CHAPTERS_CONFIG'))
|
|
missing = []
|
|
|
|
def check_chapter(chapter):
|
|
if 'file' in chapter:
|
|
if not Path(chapter['file']).exists():
|
|
missing.append(chapter['file'])
|
|
if 'sections' in chapter:
|
|
for section in chapter['sections']:
|
|
check_chapter(section)
|
|
|
|
for chapter in config['chapters']:
|
|
check_chapter(chapter)
|
|
|
|
if missing:
|
|
print('Missing chapter files:')
|
|
for f in missing:
|
|
print(f' - {f}')
|
|
sys.exit(1)
|
|
"; then
|
|
log_error "Chapter validation failed"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "All chapter files validated"
|
|
}
|
|
|
|
# Build functions
|
|
build_master() {
|
|
log_cmd "Building master document..."
|
|
|
|
if [[ "$VALIDATE" == "true" ]]; then
|
|
validate_config
|
|
validate_chapters
|
|
fi
|
|
|
|
# Check if rebuild is needed (unless forced)
|
|
if [[ "$FORCE" != "true" ]] && [[ -f "$MASTER_FILE" ]]; then
|
|
local config_time=$(stat -f %m "$CHAPTERS_CONFIG" 2>/dev/null || stat -c %Y "$CHAPTERS_CONFIG" 2>/dev/null)
|
|
local master_time=$(stat -f %m "$MASTER_FILE" 2>/dev/null || stat -c %Y "$MASTER_FILE" 2>/dev/null)
|
|
|
|
if [[ "$master_time" -gt "$config_time" ]]; then
|
|
# Check if any chapter files are newer
|
|
local rebuild_needed=false
|
|
while IFS= read -r chapter_file; do
|
|
if [[ -f "$chapter_file" ]]; then
|
|
local chapter_time=$(stat -f %m "$chapter_file" 2>/dev/null || stat -c %Y "$chapter_file" 2>/dev/null)
|
|
if [[ "$chapter_time" -gt "$master_time" ]]; then
|
|
rebuild_needed=true
|
|
break
|
|
fi
|
|
fi
|
|
done < <(python3 -c "
|
|
import json
|
|
config = json.load(open('$CHAPTERS_CONFIG'))
|
|
def get_files(chapter):
|
|
if 'file' in chapter:
|
|
print(chapter['file'])
|
|
if 'sections' in chapter:
|
|
for section in chapter['sections']:
|
|
get_files(section)
|
|
for chapter in config['chapters']:
|
|
get_files(chapter)
|
|
")
|
|
|
|
if [[ "$rebuild_needed" != "true" ]]; then
|
|
log_info "Master document is up to date"
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Run Python build script
|
|
if python3 build_guide.py "$CHAPTERS_CONFIG"; then
|
|
log_success "Master document built successfully: $MASTER_FILE"
|
|
else
|
|
log_error "Failed to build master document"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
build_pdf() {
|
|
log_cmd "Generating PDF..."
|
|
|
|
if [[ ! -f "$MASTER_FILE" ]]; then
|
|
log_error "Master document not found. Run build first."
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure md2pdf.sh is executable
|
|
chmod +x "${BUILD_DIR}/md2pdf.sh"
|
|
|
|
# Add MacTeX to PATH if available
|
|
if [[ -d "/usr/local/texlive" ]]; then
|
|
TEXLIVE_BIN=$(find /usr/local/texlive -name "xelatex" 2>/dev/null | head -1)
|
|
if [[ -n "$TEXLIVE_BIN" ]]; then
|
|
export PATH="$(dirname "$TEXLIVE_BIN"):$PATH"
|
|
fi
|
|
fi
|
|
|
|
# Change to build directory and run PDF generation
|
|
cd "$BUILD_DIR"
|
|
if ./md2pdf.sh "./Sysmon.md" "$OUTPUT_PDF"; then
|
|
log_success "PDF generated successfully: ${BUILD_DIR}/${OUTPUT_PDF}"
|
|
else
|
|
log_error "Failed to generate PDF"
|
|
exit 1
|
|
fi
|
|
cd "$SCRIPT_DIR"
|
|
}
|
|
|
|
clean_build() {
|
|
log_cmd "Cleaning build artifacts..."
|
|
|
|
local files_to_clean=(
|
|
"${BUILD_DIR}/Sysmon.md"
|
|
"${BUILD_DIR}/${OUTPUT_PDF}"
|
|
"${BUILD_DIR}/pdfgen.log"
|
|
"${BUILD_DIR}/SysmonGuide.tex"
|
|
"${BUILD_DIR}"/*.toc
|
|
"${BUILD_DIR}"/*.aux
|
|
"${BUILD_DIR}"/*.log
|
|
)
|
|
|
|
for file in "${files_to_clean[@]}"; do
|
|
if [[ -f "$file" ]] || [[ -e "$file" ]]; then
|
|
rm -f "$file"
|
|
log_verbose "Removed: $file"
|
|
fi
|
|
done
|
|
|
|
log_success "Build artifacts cleaned"
|
|
}
|
|
|
|
docker_build() {
|
|
log_cmd "Building with Docker..."
|
|
|
|
if ! command -v docker &> /dev/null; then
|
|
log_error "Docker not found. Please install Docker first."
|
|
exit 1
|
|
fi
|
|
|
|
case "$COMMAND" in
|
|
docker)
|
|
docker-compose run --rm sysmon-builder
|
|
;;
|
|
pdf)
|
|
docker-compose run --rm pdf-builder
|
|
;;
|
|
*)
|
|
docker-compose run --rm sysmon-builder
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
log_cmd "Sysmon Community Guide Build Script"
|
|
log_verbose "Command: $COMMAND"
|
|
log_verbose "Config: $CHAPTERS_CONFIG"
|
|
log_verbose "Output: $OUTPUT_PDF"
|
|
|
|
case "$COMMAND" in
|
|
build)
|
|
check_dependencies
|
|
build_master
|
|
;;
|
|
pdf)
|
|
check_dependencies
|
|
build_master
|
|
build_pdf
|
|
;;
|
|
clean)
|
|
clean_build
|
|
;;
|
|
validate)
|
|
validate_config
|
|
validate_chapters
|
|
log_success "Validation completed successfully"
|
|
;;
|
|
docker)
|
|
docker_build
|
|
;;
|
|
help)
|
|
show_help
|
|
;;
|
|
*)
|
|
log_error "Unknown command: $COMMAND"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |