Implement file preservation system for output directory cleaning

- Add sophisticated file preservation functionality to _clean_output_dir()
- Support .qsgen3_preserve file with shell glob patterns for selective preservation
- Preserve shared articles and important files during site regeneration
- Maintain backward compatibility (no preserve file = complete cleaning)
- Add comprehensive documentation in how-it-works.md
- Include example preserve file (.qsgen3_preserve.example)
- Remove legacy images directory and update documentation
- Fix workflow documentation formatting issues
This commit is contained in:
Stig-Ørjan Smelror 2025-05-31 02:04:06 +02:00
parent ed7ed0ee18
commit f72fe18873
5 changed files with 212 additions and 22 deletions

40
.qsgen3_preserve.example Normal file
View File

@ -0,0 +1,40 @@
# qsgen3 Preserve File
#
# This file specifies patterns for files that should be preserved during
# output directory cleaning. This is useful for keeping shared articles
# or other content that should remain accessible even after title changes.
#
# Format:
# - One pattern per line
# - Supports shell glob patterns (*, ?, [])
# - Lines starting with # are comments
# - Empty lines are ignored
# - Patterns are relative to the output directory
#
# Examples:
# Preserve specific files by exact name
# posts/my-important-shared-article.html
# about.html
# Preserve files by pattern
# posts/legacy-*.html
# *.pdf
# Preserve entire directories
# archive/*
# downloads/*
# Preserve files with specific extensions in certain directories
# posts/*.html
# docs/*.pdf
# Example: Preserve a specific shared article
# posts/how-to-setup-qsgen3.html
# Example: Preserve all files in a legacy directory
# legacy/*
# Example: Preserve important documents
# important-*.html
# *.pdf

View File

@ -599,9 +599,102 @@ _minify_output_directory() {
# --- Core Functions ---
_clean_output_dir() {
_log INFO "Cleaning output directory: ${QSG_CONFIG[paths_output_dir]}"
# rm -rf "${QSG_CONFIG[paths_output_dir]}"/* # Be careful with this!
# For now, just ensure it exists
mkdir -p "${QSG_CONFIG[paths_output_dir]}"
local output_dir="${QSG_CONFIG[paths_output_dir]}"
local preserve_file="$PROJECT_ROOT/.qsgen3_preserve"
# If output directory doesn't exist, just create it
if [[ ! -d "$output_dir" ]]; then
mkdir -p "$output_dir"
if [[ $? -eq 0 ]]; then
_log DEBUG "Created output directory: $output_dir"
else
_log ERROR "Failed to create output directory: $output_dir"
return 1
fi
return 0
fi
# Check if preserve file exists
if [[ -f "$preserve_file" ]]; then
_log INFO "Found preserve file: $preserve_file"
_log DEBUG "Performing selective cleaning with file preservation"
# Create temporary directory to store preserved files
local temp_preserve_dir=$(mktemp -d)
local files_preserved=0
# Read preserve patterns and backup matching files
while IFS= read -r pattern || [[ -n "$pattern" ]]; do
# Skip empty lines and comments
[[ -z "$pattern" || "$pattern" == \#* ]] && continue
# Remove leading/trailing whitespace
pattern=$(echo "$pattern" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[[ -z "$pattern" ]] && continue
_log DEBUG "Processing preserve pattern: $pattern"
# Find files matching the pattern in output directory
while IFS= read -r -d '' file; do
if [[ -f "$file" ]]; then
# Get relative path from output directory
local rel_path="${file#$output_dir/}"
local preserve_path="$temp_preserve_dir/$rel_path"
# Create directory structure in temp location
mkdir -p "$(dirname "$preserve_path")"
# Copy file to preserve location
if cp "$file" "$preserve_path"; then
files_preserved=$((files_preserved + 1))
_log DEBUG "Preserved file: $rel_path"
else
_log WARNING "Failed to preserve file: $rel_path"
fi
fi
done < <(find "$output_dir" -name "$pattern" -type f -print0 2>/dev/null)
done < "$preserve_file"
# Remove the output directory
_log DEBUG "Removing output directory contents (preserving $files_preserved files)"
rm -rf "$output_dir"
# Recreate output directory
mkdir -p "$output_dir"
if [[ $? -ne 0 ]]; then
_log ERROR "Failed to recreate output directory: $output_dir"
rm -rf "$temp_preserve_dir"
return 1
fi
# Restore preserved files
if [[ $files_preserved -gt 0 ]]; then
_log DEBUG "Restoring $files_preserved preserved files"
if [[ -d "$temp_preserve_dir" ]]; then
# Copy preserved files back, maintaining directory structure
(cd "$temp_preserve_dir" && find . -type f -exec cp --parents {} "$output_dir/" \; 2>/dev/null)
_log INFO "Restored $files_preserved preserved files"
fi
fi
# Clean up temporary directory
rm -rf "$temp_preserve_dir"
else
# No preserve file - do complete cleaning as before
_log DEBUG "No preserve file found - performing complete cleaning"
rm -rf "$output_dir"
mkdir -p "$output_dir"
if [[ $? -ne 0 ]]; then
_log ERROR "Failed to recreate output directory: $output_dir"
return 1
fi
fi
_log DEBUG "Output directory cleaning completed successfully"
return 0
}
_copy_static_files() {
@ -1168,9 +1261,9 @@ _generate_rss_feed() {
continue
fi
local title=$(echo "$frontmatter" | grep -m1 -iE '^title:' | sed -E 's/^title:[[:space:]]*//i; s/^["\x27](.*)["\x27]$/\1/')
local date_iso=$(echo "$frontmatter" | grep -m1 -iE '^date:' | sed -E 's/^date:[[:space:]]*//i; s/^["\x27](.*)["\x27]$/\1/')
local summary=$(echo "$frontmatter" | grep -m1 -iE '^summary:' | sed -E 's/^summary:[[:space:]]*//i; s/^["\x27](.*)["\x27]$/\1/')
local title=$(echo "$frontmatter" | grep -m1 -iE '^title:' | sed -E 's/^title:[[:space:]]*//i; s/^[\"\x27](.*)[\"\x27]$/\1/')
local date_iso=$(echo "$frontmatter" | grep -m1 -iE '^date:' | sed -E 's/^date:[[:space:]]*//i; s/^[\"\x27](.*)[\"\x27]$/\1/')
local summary=$(echo "$frontmatter" | grep -m1 -iE '^summary:' | sed -E 's/^summary:[[:space:]]*//i; s/^[\"\x27](.*)[\"\x27]$/\1/')
local draft=$(echo "$frontmatter" | grep -m1 -iE '^draft:' | sed -E 's/^draft:[[:space:]]*//i' | tr '[:upper:]' '[:lower:]')
if [[ "$draft" == "true" && "${QSG_CONFIG[build_options_process_drafts]}" != "true" ]]; then

View File

@ -42,6 +42,7 @@ project-root/
├── bin/
│ └── qsgen3 # Main generator script
├── site.conf # Main configuration file
├── .qsgen3_preserve # Optional: File preservation patterns
├── content/ # Markdown content
│ ├── posts/ # Blog posts
│ │ └── hello-world.md
@ -56,7 +57,6 @@ project-root/
├── themes/ # Theme directory
│ └── theme-name/ # Individual theme
│ ├── layouts/ # Theme-specific templates (optional)
│ ├── static/ # Theme static assets (optional)
│ └── css/ # Alternative theme asset location
└── output/ # Generated site (created by qsgen3)
├── index.html
@ -128,8 +128,7 @@ themes/theme-name/
│ └── rss.xml # RSS template
├── static/ # Preferred: Standard static assets location
│ ├── css/
│ ├── js/
│ └── images/
│ └── js/
└── css/ # Alternative: Direct CSS location
└── style.css
```
@ -180,10 +179,7 @@ themes/my-theme/
└── static/ # Static assets
├── css/
│ └── style.css # Main stylesheet
├── js/ # JavaScript files (optional)
│ └── theme.js
└── images/ # Theme images (optional)
└── logo.png
└── js/ # JavaScript files (optional)
```
**Option B: CSS-Only Structure**
@ -744,8 +740,7 @@ output/
│ └── post-name.html
├── static/ # All static assets
│ ├── css/ # Stylesheets
│ ├── js/ # JavaScript (if provided by theme)
│ └── images/ # Images and media
│ └── js/ # JavaScript (if provided by theme)
└── css/ # Legacy: Index-specific CSS location
└── theme.css # Copy of main theme CSS for index page
```
@ -870,11 +865,52 @@ Process Theme Configuration
```
Prepare Output Directory
├── Clean existing output directory
├── Create fresh output directory structure
└── Prepare for static file copying
├── Check for .qsgen3_preserve file in project root
├── If preserve file exists:
│ ├── Read file patterns (shell glob patterns)
│ ├── Create temporary backup directory
│ ├── Find and backup matching files from output directory
│ ├── Remove entire output directory
│ ├── Recreate clean output directory
│ ├── Restore preserved files maintaining directory structure
│ └── Clean up temporary backup directory
│ └── Remove entire output directory
│ └── Create fresh output directory
└── Log preservation and cleaning operations
```
#### File Preservation System
qsgen3 supports preserving specific files during the cleaning process to handle cases where content has been shared or bookmarked and should remain accessible even after title changes.
**Preserve File Format (`.qsgen3_preserve`):**
- Located in project root directory
- One pattern per line using shell glob patterns (`*`, `?`, `[]`)
- Lines starting with `#` are comments
- Empty lines are ignored
- Patterns are relative to the output directory
**Example preserve patterns:**
```bash
# Preserve specific shared articles
posts/my-important-shared-article.html
posts/viral-blog-post.html
# Preserve files by pattern
posts/legacy-*.html
archive/*
# Preserve all PDFs and downloads
*.pdf
downloads/*
```
**Benefits:**
- Maintains stable URLs for shared content
- Prevents broken links when content is renamed
- Flexible pattern matching for various preservation needs
- Backward compatible (no preserve file = complete cleaning)
### 6. Static File Processing
```
@ -999,6 +1035,31 @@ Complete Generation
- Verify available disk space
- Review path configurations for absolute vs. relative paths
#### 5. File Preservation Issues
**Symptoms**: Expected files not preserved during cleaning, or preservation not working
**Causes**:
- Incorrect patterns in `.qsgen3_preserve` file
- File paths don't match patterns
- Permission issues with temporary backup directory
- Malformed preserve file format
**Solutions**:
- Verify patterns use shell glob syntax (`*`, `?`, `[]`)
- Check that patterns are relative to output directory
- Ensure `.qsgen3_preserve` file is in project root
- Test patterns with `find output/ -name "pattern"` before adding to preserve file
- Enable debug logging to see preservation process details
- Verify file permissions allow temporary directory creation
**Example debugging:**
```bash
# Test if your pattern matches files
find output/ -name "posts/legacy-*.html"
# Enable debug logging to see preservation process
QSG_DEBUG=1 ./bin/qsgen3
```
### Debug Logging
Enable detailed logging by modifying the `_log` function or adding debug statements:

View File

@ -1,3 +0,0 @@
Copy this directory to your **www_root**
**$www_root/images/**

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" zoomAndPan="magnify" viewBox="0 0 224.87999 225" height="300" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="ec549c68f3"><path d="M 44.976562 24 L 201 24 L 201 179.964844 L 44.976562 179.964844 Z M 44.976562 24 " clip-rule="nonzero"/></clipPath><clipPath id="f9fb9407ba"><path d="M 109 0.0585938 L 224.761719 0.0585938 L 224.761719 116 L 109 116 Z M 109 0.0585938 " clip-rule="nonzero"/></clipPath></defs><g clip-path="url(#ec549c68f3)"><path fill="#000000" d="M 191.742188 84.863281 C 187.179688 84.863281 183.46875 88.574219 183.46875 93.136719 L 183.46875 163.425781 L 61.507812 163.425781 L 61.507812 41.460938 L 131.792969 41.460938 C 136.359375 41.460938 140.070312 37.75 140.070312 33.1875 C 140.070312 28.625 136.359375 24.914062 131.792969 24.914062 L 53.25 24.914062 C 48.6875 24.914062 44.976562 28.625 44.976562 33.1875 L 44.976562 171.679688 C 44.976562 176.242188 48.6875 179.953125 53.25 179.953125 L 191.742188 179.953125 C 196.308594 179.953125 200.019531 176.242188 200.019531 171.679688 L 200.019531 93.136719 C 200.019531 88.574219 196.308594 84.863281 191.742188 84.863281 Z M 191.742188 84.863281 " fill-opacity="1" fill-rule="nonzero"/></g><g clip-path="url(#f9fb9407ba)"><path fill="#000000" d="M 216.585938 0.0703125 L 166.710938 0.0703125 C 162.148438 0.0703125 158.4375 3.78125 158.4375 8.34375 C 158.4375 12.90625 162.128906 16.617188 166.710938 16.617188 L 196.628906 16.617188 L 111.4375 101.789062 C 108.199219 105.027344 108.199219 110.253906 111.4375 113.472656 C 113.046875 115.082031 115.167969 115.898438 117.289062 115.898438 C 119.410156 115.898438 121.53125 115.082031 123.140625 113.472656 L 208.332031 28.28125 L 208.332031 58.21875 C 208.332031 62.78125 212.023438 66.496094 216.605469 66.496094 C 221.167969 66.496094 224.878906 62.78125 224.878906 58.21875 L 224.878906 8.34375 C 224.859375 3.78125 221.148438 0.0703125 216.585938 0.0703125 Z M 216.585938 0.0703125 " fill-opacity="1" fill-rule="nonzero"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB