jekyll-theme-zer0 0.22.0 → 0.22.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +236 -0
  3. data/README.md +66 -19
  4. data/_data/navigation/admin.yml +53 -0
  5. data/_data/theme_backgrounds.yml +121 -0
  6. data/_includes/components/admin-tabs.html +59 -0
  7. data/_includes/components/analytics-dashboard.html +232 -0
  8. data/_includes/components/background-customizer.html +159 -0
  9. data/_includes/components/background-settings.html +137 -0
  10. data/_includes/components/collection-manager.html +151 -0
  11. data/_includes/components/component-showcase.html +452 -0
  12. data/_includes/components/config-editor.html +207 -0
  13. data/_includes/components/config-viewer.html +479 -0
  14. data/_includes/components/env-dashboard.html +154 -0
  15. data/_includes/components/feature-card.html +94 -0
  16. data/_includes/components/info-section.html +172 -149
  17. data/_includes/components/js-cdn.html +4 -1
  18. data/_includes/components/nav-editor.html +99 -0
  19. data/_includes/components/setup-banner.html +28 -0
  20. data/_includes/components/setup-check.html +53 -0
  21. data/_includes/components/svg-background.html +42 -0
  22. data/_includes/components/theme-customizer.html +46 -0
  23. data/_includes/content/seo.html +68 -135
  24. data/_includes/core/footer.html +1 -1
  25. data/_includes/core/head.html +3 -2
  26. data/_includes/core/header.html +14 -7
  27. data/_includes/landing/landing-install-cards.html +18 -7
  28. data/_includes/navigation/admin-nav.html +95 -0
  29. data/_includes/navigation/navbar.html +43 -5
  30. data/_includes/navigation/sidebar-left.html +1 -1
  31. data/_includes/setup/wizard.html +330 -0
  32. data/_layouts/admin.html +166 -0
  33. data/_layouts/landing.html +23 -9
  34. data/_layouts/root.html +12 -6
  35. data/_layouts/setup.html +73 -0
  36. data/_plugins/preview_image_generator.rb +26 -12
  37. data/_sass/core/_navbar.scss +2 -2
  38. data/_sass/custom.scss +28 -6
  39. data/_sass/theme/_background-mixins.scss +95 -0
  40. data/_sass/theme/_backgrounds.scss +156 -0
  41. data/_sass/theme/_color-modes.scss +2 -1
  42. data/assets/backgrounds/gradients/air.svg +15 -0
  43. data/assets/backgrounds/gradients/aqua.svg +15 -0
  44. data/assets/backgrounds/gradients/contrast.svg +15 -0
  45. data/assets/backgrounds/gradients/dark.svg +15 -0
  46. data/assets/backgrounds/gradients/dirt.svg +15 -0
  47. data/assets/backgrounds/gradients/mint.svg +15 -0
  48. data/assets/backgrounds/gradients/neon.svg +15 -0
  49. data/assets/backgrounds/gradients/plum.svg +15 -0
  50. data/assets/backgrounds/gradients/sunrise.svg +15 -0
  51. data/assets/backgrounds/noise/air.svg +8 -0
  52. data/assets/backgrounds/noise/aqua.svg +8 -0
  53. data/assets/backgrounds/noise/contrast.svg +8 -0
  54. data/assets/backgrounds/noise/dark.svg +8 -0
  55. data/assets/backgrounds/noise/dirt.svg +8 -0
  56. data/assets/backgrounds/noise/mint.svg +8 -0
  57. data/assets/backgrounds/noise/neon.svg +8 -0
  58. data/assets/backgrounds/noise/plum.svg +8 -0
  59. data/assets/backgrounds/noise/sunrise.svg +8 -0
  60. data/assets/backgrounds/patterns/air.svg +7 -0
  61. data/assets/backgrounds/patterns/aqua.svg +7 -0
  62. data/assets/backgrounds/patterns/contrast.svg +4 -0
  63. data/assets/backgrounds/patterns/dark.svg +5 -0
  64. data/assets/backgrounds/patterns/dirt.svg +5 -0
  65. data/assets/backgrounds/patterns/mint.svg +6 -0
  66. data/assets/backgrounds/patterns/neon.svg +6 -0
  67. data/assets/backgrounds/patterns/plum.svg +6 -0
  68. data/assets/backgrounds/patterns/sunrise.svg +5 -0
  69. data/assets/js/background-customizer.js +73 -0
  70. data/assets/js/code-copy.js +18 -47
  71. data/assets/js/config-utility.js +307 -0
  72. data/assets/js/nav-editor.js +39 -0
  73. data/assets/js/palette-generator.js +415 -0
  74. data/assets/js/search-modal.js +31 -11
  75. data/assets/js/setup-wizard.js +306 -0
  76. data/assets/js/skin-editor.js +645 -0
  77. data/assets/js/theme-customizer.js +102 -0
  78. data/assets/js/ui-enhancements.js +15 -24
  79. data/assets/vendor/bootstrap/css/bootstrap.min.css +1 -0
  80. data/assets/vendor/bootstrap/js/bootstrap.bundle.min.js +1 -0
  81. data/scripts/README.md +45 -0
  82. data/scripts/features/generate-preview-images +297 -7
  83. data/scripts/features/install-preview-generator +51 -33
  84. data/scripts/fork-cleanup.sh +92 -19
  85. data/scripts/github-setup.sh +284 -0
  86. data/scripts/init_setup.sh +0 -1
  87. data/scripts/lib/frontmatter.sh +543 -0
  88. data/scripts/lib/migrate.sh +265 -0
  89. data/scripts/lib/preview_generator.py +607 -32
  90. data/scripts/lint-pages +505 -0
  91. data/scripts/migrate.sh +201 -0
  92. data/scripts/platform/setup-linux.sh +244 -0
  93. data/scripts/platform/setup-macos.sh +187 -0
  94. data/scripts/platform/setup-wsl.sh +196 -0
  95. metadata +71 -6
@@ -39,13 +39,15 @@ REPO_NAME="zer0-mistakes"
39
39
  REPO_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}"
40
40
  RAW_URL="https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/main"
41
41
 
42
- # Files to install
43
- declare -a SCRIPT_FILES=(
44
- "scripts/generate-preview-images.sh"
42
+ # Files to install: source_path -> destination_path (parallel arrays)
43
+ SOURCE_FILES=(
44
+ "scripts/features/generate-preview-images"
45
45
  "scripts/lib/preview_generator.py"
46
+ "_plugins/preview_image_generator.rb"
46
47
  )
47
-
48
- declare -a PLUGIN_FILES=(
48
+ DEST_FILES=(
49
+ "scripts/generate-preview-images.sh"
50
+ "scripts/lib/preview_generator.py"
49
51
  "_plugins/preview_image_generator.rb"
50
52
  )
51
53
 
@@ -55,6 +57,7 @@ FORCE=false
55
57
  DEFAULT_PROVIDER="openai"
56
58
  INSTALL_CONFIG=true
57
59
  INSTALL_TASKS=true
60
+ LOCAL_THEME_DIR=""
58
61
 
59
62
  # Colors for output
60
63
  RED='\033[0;31m'
@@ -96,6 +99,7 @@ ${YELLOW}OPTIONS:${NC}
96
99
  -p, --provider PROVIDER Set default AI provider (openai, stability, local)
97
100
  --no-config Skip _config.yml modification
98
101
  --no-tasks Skip VS Code tasks installation
102
+ -l, --local PATH Copy files from local theme directory instead of downloading
99
103
 
100
104
  ${YELLOW}EXAMPLES:${NC}
101
105
  # Install with defaults
@@ -159,42 +163,58 @@ check_dependencies() {
159
163
  log "All dependencies satisfied"
160
164
  }
161
165
 
162
- # Download a file from the repository
163
- download_file() {
164
- local remote_path="$1"
165
- local local_path="$2"
166
- local url="${RAW_URL}/${remote_path}"
166
+ # Install a file (download from repo or copy from local theme)
167
+ install_file() {
168
+ local source_path="$1"
169
+ local dest_path="$2"
167
170
 
168
171
  if [[ "$DRY_RUN" == true ]]; then
169
- dry_run_log "Would download: $url $local_path"
172
+ if [[ -n "$LOCAL_THEME_DIR" ]]; then
173
+ dry_run_log "Would copy: $LOCAL_THEME_DIR/$source_path → $dest_path"
174
+ else
175
+ dry_run_log "Would download: ${RAW_URL}/$source_path → $dest_path"
176
+ fi
170
177
  return 0
171
178
  fi
172
179
 
173
180
  # Create directory if needed
174
- local dir=$(dirname "$local_path")
181
+ local dir=$(dirname "$dest_path")
175
182
  if [[ ! -d "$dir" ]]; then
176
183
  mkdir -p "$dir"
177
184
  fi
178
185
 
179
186
  # Check if file exists and not forcing
180
- if [[ -f "$local_path" && "$FORCE" != true ]]; then
181
- warn "File exists, skipping: $local_path (use --force to overwrite)"
187
+ if [[ -f "$dest_path" && "$FORCE" != true ]]; then
188
+ warn "File exists, skipping: $dest_path (use --force to overwrite)"
182
189
  return 0
183
190
  fi
184
191
 
185
- step "Downloading: $remote_path"
186
- if curl -fsSL "$url" -o "$local_path"; then
187
- log "Installed: $local_path"
188
-
189
- # Make scripts executable
190
- if [[ "$local_path" == *.sh ]]; then
191
- chmod +x "$local_path"
192
+ if [[ -n "$LOCAL_THEME_DIR" ]]; then
193
+ # Copy from local theme directory
194
+ local local_src="${LOCAL_THEME_DIR}/${source_path}"
195
+ if [[ ! -f "$local_src" ]]; then
196
+ error "Source file not found: $local_src"
197
+ return 1
192
198
  fi
193
- return 0
199
+ step "Copying: $source_path → $dest_path"
200
+ cp "$local_src" "$dest_path"
194
201
  else
195
- error "Failed to download: $url"
196
- return 1
202
+ # Download from repository
203
+ local url="${RAW_URL}/${source_path}"
204
+ step "Downloading: $source_path"
205
+ if ! curl -fsSL "$url" -o "$dest_path"; then
206
+ error "Failed to download: $url"
207
+ return 1
208
+ fi
197
209
  fi
210
+
211
+ log "Installed: $dest_path"
212
+
213
+ # Make scripts executable
214
+ if [[ "$dest_path" == *.sh ]] || head -1 "$dest_path" 2>/dev/null | grep -q '^#!/'; then
215
+ chmod +x "$dest_path"
216
+ fi
217
+ return 0
198
218
  }
199
219
 
200
220
  # Add configuration to _config.yml
@@ -459,15 +479,9 @@ main() {
459
479
  echo ""
460
480
 
461
481
  # Installation steps
462
- info "Installing scripts..."
463
- for file in "${SCRIPT_FILES[@]}"; do
464
- download_file "$file" "$file"
465
- done
466
- echo ""
467
-
468
- info "Installing Jekyll plugin..."
469
- for file in "${PLUGIN_FILES[@]}"; do
470
- download_file "$file" "$file"
482
+ info "Installing preview generator files..."
483
+ for i in "${!SOURCE_FILES[@]}"; do
484
+ install_file "${SOURCE_FILES[$i]}" "${DEST_FILES[$i]}"
471
485
  done
472
486
  echo ""
473
487
 
@@ -518,6 +532,10 @@ while [[ $# -gt 0 ]]; do
518
532
  --no-tasks)
519
533
  INSTALL_TASKS=false
520
534
  ;;
535
+ -l|--local)
536
+ LOCAL_THEME_DIR="$2"
537
+ shift
538
+ ;;
521
539
  *)
522
540
  error "Unknown option: $1"
523
541
  show_help
@@ -20,8 +20,15 @@
20
20
  #
21
21
  # This script can be run:
22
22
  # 1. After using "Use this template" button on GitHub
23
- # 2. After manually forking the repository
24
- # 3. Via install.sh --fork mode
23
+ # 2. After manually forking the repository (recommended: into
24
+ # <username>.github.io for a GitHub Pages user site)
25
+ # 3. After cloning your fork locally — see docs/FORKING.md
26
+ #
27
+ # Companion files:
28
+ # - templates/cleanup/remove-paths.txt (paths to delete)
29
+ # - templates/cleanup/reset-fields.yml (config field replacements)
30
+ # - templates/data/authors.yml.template (authors file replacement)
31
+ # - templates/pages/welcome-post.md.template (first blog post)
25
32
  # =========================================================================
26
33
 
27
34
  set -euo pipefail
@@ -68,6 +75,7 @@ VERBOSE="${VERBOSE:-false}"
68
75
  # Paths
69
76
  TEMPLATES_DIR="$REPO_ROOT/templates"
70
77
  REMOVE_PATHS_FILE="$TEMPLATES_DIR/cleanup/remove-paths.txt"
78
+ RESET_FIELDS_FILE="$TEMPLATES_DIR/cleanup/reset-fields.yml"
71
79
 
72
80
  # -------------------------------------------------------------------------
73
81
  # Argument Parsing
@@ -309,7 +317,7 @@ create_welcome_post() {
309
317
  # Fallback embedded content
310
318
  cat > "$post_file" << EOF
311
319
  ---
312
- layout: journals
320
+ layout: article
313
321
  title: "Welcome to Your New Site"
314
322
  date: $post_date
315
323
  categories: [General]
@@ -337,6 +345,33 @@ EOF
337
345
  }
338
346
 
339
347
  # Reset _config.yml with placeholder values
348
+ get_reset_field_value() {
349
+ local file_key="$1"
350
+ local field_key="$2"
351
+ local default_value="${3:-}"
352
+
353
+ if [[ ! -f "$RESET_FIELDS_FILE" ]] || ! command -v ruby >/dev/null 2>&1; then
354
+ printf '%s' "$default_value"
355
+ return 0
356
+ fi
357
+
358
+ local value
359
+ if value="$(ruby -ryaml -e '
360
+ data = YAML.safe_load_file(ARGV[0], aliases: true) || {}
361
+ section = data[ARGV[1]] || {}
362
+ if section.is_a?(Hash) && section.key?(ARGV[2])
363
+ current = section[ARGV[2]]
364
+ print(current == true ? "true" : current == false ? "false" : current.to_s)
365
+ exit 0
366
+ end
367
+ exit 1
368
+ ' "$RESET_FIELDS_FILE" "$file_key" "$field_key" 2>/dev/null)"; then
369
+ printf '%s' "$value"
370
+ else
371
+ printf '%s' "$default_value"
372
+ fi
373
+ }
374
+
340
375
  reset_config() {
341
376
  info "Resetting _config.yml..."
342
377
 
@@ -355,22 +390,60 @@ reset_config() {
355
390
  # Create backup
356
391
  cp "$config_file" "${config_file}.backup.$(date +%Y%m%d%H%M%S)"
357
392
 
358
- # Use sed to replace specific values
359
- # Site identity
360
- sed -i.tmp "s/^founder:.*/founder: \"$SITE_AUTHOR\"/" "$config_file"
361
- sed -i.tmp "s/^github_user:.*/github_user: \"$GITHUB_USER_INPUT\"/" "$config_file"
362
- sed -i.tmp "s/^title:.*/title: \"$SITE_TITLE\"/" "$config_file"
363
- sed -i.tmp "s/^description:.*/description: \"$SITE_DESCRIPTION\"/" "$config_file"
364
- sed -i.tmp "s/^author:.*/author: \"$SITE_AUTHOR\"/" "$config_file"
365
- sed -i.tmp "s/^email:.*/email: \"$SITE_EMAIL\"/" "$config_file"
366
- sed -i.tmp "s/^name:.*/name: \"$SITE_AUTHOR\"/" "$config_file"
367
-
368
- # Clear analytics
369
- sed -i.tmp "s/^google_analytics:.*/google_analytics: \"\"/" "$config_file"
370
-
371
- # Clear domain for auto-detection
372
- sed -i.tmp "s/^domain:.*/domain: \"\"/" "$config_file"
373
- sed -i.tmp "s/^url:.*/url: \"\"/" "$config_file"
393
+ # Derive repository name from the git remote or current directory.
394
+ # Under `set -euo pipefail`, missing `origin` should not terminate.
395
+ local repo_name
396
+ local remote_url
397
+ remote_url="$(git -C "$REPO_ROOT" remote get-url origin 2>/dev/null || true)"
398
+
399
+ if [[ -n "$remote_url" ]]; then
400
+ local remote_url_clean
401
+ remote_url_clean="${remote_url%.git}"
402
+ repo_name="$(basename "$remote_url_clean")"
403
+ else
404
+ repo_name="$(basename "$REPO_ROOT")"
405
+ fi
406
+
407
+ local posthog_enabled_reset posthog_api_key_reset
408
+ posthog_enabled_reset="$(get_reset_field_value "_config.yml" "posthog.enabled" "false")"
409
+ posthog_api_key_reset="$(get_reset_field_value "_config.yml" "posthog.api_key" "")"
410
+
411
+ local giscus_enabled_reset giscus_repo_id_reset giscus_category_id_reset
412
+ giscus_enabled_reset="$(get_reset_field_value "_config.yml" "giscus.enabled" "false")"
413
+ giscus_repo_id_reset="$(get_reset_field_value "_config.yml" "giscus.data-repo-id" "")"
414
+ giscus_category_id_reset="$(get_reset_field_value "_config.yml" "giscus.data-category-id" "")"
415
+
416
+ # Note: sed patterns use [[:space:]]*: to match the YAML format where
417
+ # there may be spaces between the key name and the colon.
418
+ # Anchors (&anchor_name) are preserved where they exist so that
419
+ # YAML alias references (*anchor_name) throughout the file keep working.
420
+
421
+ # --- Site identity (preserve YAML anchors) ---
422
+ sed -i.tmp "s|^\(founder[[:space:]]*:[[:space:]]*\).*|\1\"$SITE_AUTHOR\"|" "$config_file"
423
+ sed -i.tmp "s|^\(github_user[[:space:]]*:[[:space:]]*&github_user[[:space:]]*\).*|\1\"$GITHUB_USER_INPUT\"|" "$config_file"
424
+ sed -i.tmp "s|^\(repository_name[[:space:]]*:[[:space:]]*&github_repository[[:space:]]*\).*|\1\"$repo_name\"|" "$config_file"
425
+ sed -i.tmp "s|^\(local_repo[[:space:]]*:[[:space:]]*&local_repo[[:space:]]*\).*|\1\"$repo_name\"|" "$config_file"
426
+ sed -i.tmp "s|^\(title[[:space:]]*:[[:space:]]*&title[[:space:]]*\).*|\1\"$SITE_TITLE\"|" "$config_file"
427
+ sed -i.tmp "s|^\(name[[:space:]]*:[[:space:]]*&name[[:space:]]*\).*|\1\"$SITE_AUTHOR\"|" "$config_file"
428
+ sed -i.tmp "s|^\(email[[:space:]]*:[[:space:]]*\)\"[^\"]*\"|\1\"$SITE_EMAIL\"|" "$config_file"
429
+
430
+ # --- URLs (set for username.github.io user site deployment) ---
431
+ sed -i.tmp "s|^\(domain[[:space:]]*:[[:space:]]*&domain[[:space:]]*\).*|\1\"github\"|" "$config_file"
432
+ sed -i.tmp "s|^\(domain_ext[[:space:]]*:[[:space:]]*&domain_ext[[:space:]]*\).*|\1\"io\"|" "$config_file"
433
+ sed -i.tmp "s|^\(url[[:space:]]*:[[:space:]]*&url[[:space:]]*\).*|\1\"https://${GITHUB_USER_INPUT}.github.io\"|" "$config_file"
434
+ # baseurl stays empty — forking into username.github.io means the site
435
+ # deploys at the domain root, so no baseurl prefix is needed.
436
+
437
+ # --- Analytics (MUST be cleared for forks) ---
438
+ sed -i.tmp "s|^\(google_analytics[[:space:]]*:[[:space:]]*\).*|\1\"\"|" "$config_file"
439
+ # Disable PostHog in the posthog block only
440
+ sed -i.tmp "/^posthog[[:space:]]*:/,/^[^[:space:]]/ s|^\([[:space:]]*enabled[[:space:]]*:[[:space:]]*\).*|\1${posthog_enabled_reset}|" "$config_file"
441
+ sed -i.tmp "/^posthog[[:space:]]*:/,/^[^[:space:]]/ s|^\([[:space:]]*api_key[[:space:]]*:[[:space:]]*\).*|\1'${posthog_api_key_reset}'|" "$config_file"
442
+
443
+ # --- Comments (MUST be cleared for forks) ---
444
+ sed -i.tmp "/^giscus[[:space:]]*:/,/^[^[:space:]]/ s|^\([[:space:]]*enabled[[:space:]]*:[[:space:]]*\).*|\1${giscus_enabled_reset}|" "$config_file"
445
+ sed -i.tmp "/^giscus[[:space:]]*:/,/^[^[:space:]]/ s|^\([[:space:]]*data-repo-id[[:space:]]*:[[:space:]]*\).*|\1\"${giscus_repo_id_reset}\"|" "$config_file"
446
+ sed -i.tmp "/^giscus[[:space:]]*:/,/^[^[:space:]]/ s|^\([[:space:]]*data-category-id[[:space:]]*:[[:space:]]*\).*|\1\"${giscus_category_id_reset}\"|" "$config_file"
374
447
 
375
448
  # Clean up temp files
376
449
  rm -f "${config_file}.tmp"
@@ -0,0 +1,284 @@
1
+ #!/bin/bash
2
+ # =========================================================================
3
+ # Zer0-Mistakes GitHub Setup
4
+ # =========================================================================
5
+ # Fork or clone zer0-mistakes via the GitHub CLI (gh), then hand off to
6
+ # install.sh --fork for cleanup and personalisation.
7
+ #
8
+ # Usage:
9
+ # ./scripts/github-setup.sh [OPTIONS]
10
+ #
11
+ # Options:
12
+ # --repo-name NAME Name for the new repository (default: my-site)
13
+ # --visibility public|private Repository visibility (default: public)
14
+ # --github-user USER GitHub username override
15
+ # --site-name NAME Site title
16
+ # --author NAME Author name
17
+ # --email EMAIL Contact email
18
+ # --clone-only Clone without forking (use original repo)
19
+ # --non-interactive Skip prompts
20
+ # -h, --help Show this help
21
+ # =========================================================================
22
+
23
+ set -euo pipefail
24
+
25
+ # -------------------------------------------------------------------------
26
+ # Load shared config if available
27
+ # -------------------------------------------------------------------------
28
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29
+ if [[ -f "$SCRIPT_DIR/../templates/config/install.conf" ]]; then
30
+ # shellcheck source=/dev/null
31
+ source "$SCRIPT_DIR/../templates/config/install.conf"
32
+ fi
33
+
34
+ THEME_NAME="${THEME_NAME:-zer0-mistakes}"
35
+ GITHUB_REPO="${GITHUB_REPO:-bamr87/zer0-mistakes}"
36
+ GITHUB_URL="${GITHUB_URL:-https://github.com/bamr87/zer0-mistakes}"
37
+
38
+ # Defaults
39
+ REPO_NAME=""
40
+ VISIBILITY="public"
41
+ GH_USER=""
42
+ SITE_NAME=""
43
+ AUTHOR=""
44
+ EMAIL=""
45
+ CLONE_ONLY=false
46
+ NON_INTERACTIVE=false
47
+
48
+ # Colours (reuse or define)
49
+ RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
50
+ log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
51
+ log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
52
+ log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
53
+ log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
54
+
55
+ # -------------------------------------------------------------------------
56
+ # Parse arguments
57
+ # -------------------------------------------------------------------------
58
+ parse_args() {
59
+ while [[ $# -gt 0 ]]; do
60
+ case $1 in
61
+ --repo-name) REPO_NAME="$2"; shift 2 ;;
62
+ --visibility) VISIBILITY="$2"; shift 2 ;;
63
+ --github-user) GH_USER="$2"; shift 2 ;;
64
+ --site-name) SITE_NAME="$2"; shift 2 ;;
65
+ --author) AUTHOR="$2"; shift 2 ;;
66
+ --email) EMAIL="$2"; shift 2 ;;
67
+ --clone-only) CLONE_ONLY=true; shift ;;
68
+ --non-interactive) NON_INTERACTIVE=true; shift ;;
69
+ -h|--help) show_help; exit 0 ;;
70
+ *) log_error "Unknown option: $1"; show_help; exit 1 ;;
71
+ esac
72
+ done
73
+ }
74
+
75
+ show_help() {
76
+ echo "Usage: $0 [OPTIONS]"
77
+ echo
78
+ echo "Fork or clone zer0-mistakes via GitHub CLI."
79
+ echo
80
+ echo "Options:"
81
+ echo " --repo-name NAME Repository name (default: my-site)"
82
+ echo " --visibility public|private (default: public)"
83
+ echo " --github-user USER GitHub username override"
84
+ echo " --site-name NAME Site title for _config.yml"
85
+ echo " --author NAME Author name"
86
+ echo " --email EMAIL Contact email"
87
+ echo " --clone-only Clone without forking"
88
+ echo " --non-interactive Skip prompts"
89
+ echo " -h, --help Show this help"
90
+ }
91
+
92
+ # -------------------------------------------------------------------------
93
+ # Prerequisite checks
94
+ # -------------------------------------------------------------------------
95
+ check_gh_cli() {
96
+ if ! command -v gh &>/dev/null; then
97
+ log_error "GitHub CLI (gh) is not installed."
98
+ echo
99
+ log_info "Install it:"
100
+ echo " macOS: brew install gh"
101
+ echo " Linux: https://github.com/cli/cli/blob/trunk/docs/install_linux.md"
102
+ echo " Windows: winget install --id GitHub.cli"
103
+ echo
104
+ return 1
105
+ fi
106
+ return 0
107
+ }
108
+
109
+ check_gh_auth() {
110
+ if ! gh auth status &>/dev/null 2>&1; then
111
+ log_warning "Not authenticated with GitHub CLI."
112
+ log_info "Run: gh auth login"
113
+ if [[ "$NON_INTERACTIVE" == true ]]; then
114
+ return 1
115
+ fi
116
+ gh auth login
117
+ fi
118
+ return 0
119
+ }
120
+
121
+ # -------------------------------------------------------------------------
122
+ # Gather user values interactively
123
+ # -------------------------------------------------------------------------
124
+ gather_values() {
125
+ local detected_user
126
+ detected_user="$(gh api user --jq '.login' 2>/dev/null || echo "")"
127
+
128
+ if [[ -z "$GH_USER" ]]; then
129
+ if [[ "$NON_INTERACTIVE" == true ]]; then
130
+ GH_USER="${detected_user:-your-username}"
131
+ else
132
+ read -r -p "GitHub username [${detected_user:-your-username}]: " GH_USER
133
+ GH_USER="${GH_USER:-${detected_user:-your-username}}"
134
+ fi
135
+ fi
136
+
137
+ if [[ -z "$REPO_NAME" ]]; then
138
+ if [[ "$NON_INTERACTIVE" == true ]]; then
139
+ REPO_NAME="my-site"
140
+ else
141
+ read -r -p "Repository name [my-site]: " REPO_NAME
142
+ REPO_NAME="${REPO_NAME:-my-site}"
143
+ fi
144
+ fi
145
+
146
+ # Sanitise repo name
147
+ REPO_NAME="$(echo "$REPO_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd '[:alnum:]-')"
148
+
149
+ if [[ -z "$SITE_NAME" ]]; then
150
+ if [[ "$NON_INTERACTIVE" == true ]]; then
151
+ SITE_NAME="My Jekyll Site"
152
+ else
153
+ read -r -p "Site title [My Jekyll Site]: " SITE_NAME
154
+ SITE_NAME="${SITE_NAME:-My Jekyll Site}"
155
+ fi
156
+ fi
157
+
158
+ if [[ -z "$AUTHOR" ]]; then
159
+ local git_name
160
+ git_name="$(git config --global user.name 2>/dev/null || echo "")"
161
+ if [[ "$NON_INTERACTIVE" == true ]]; then
162
+ AUTHOR="${git_name:-Site Author}"
163
+ else
164
+ read -r -p "Author name [${git_name:-Site Author}]: " AUTHOR
165
+ AUTHOR="${AUTHOR:-${git_name:-Site Author}}"
166
+ fi
167
+ fi
168
+
169
+ if [[ -z "$EMAIL" ]]; then
170
+ local git_email
171
+ git_email="$(git config --global user.email 2>/dev/null || echo "")"
172
+ if [[ "$NON_INTERACTIVE" == true ]]; then
173
+ EMAIL="${git_email:-your@email.com}"
174
+ else
175
+ read -r -p "Email [${git_email:-your@email.com}]: " EMAIL
176
+ EMAIL="${EMAIL:-${git_email:-your@email.com}}"
177
+ fi
178
+ fi
179
+
180
+ echo
181
+ log_info "Configuration:"
182
+ echo " GitHub user : $GH_USER"
183
+ echo " Repo name : $REPO_NAME"
184
+ echo " Site title : $SITE_NAME"
185
+ echo " Author : $AUTHOR"
186
+ echo " Email : $EMAIL"
187
+ echo " Visibility : $VISIBILITY"
188
+ echo " Mode : $(if $CLONE_ONLY; then echo clone; else echo fork; fi)"
189
+ echo
190
+ }
191
+
192
+ # -------------------------------------------------------------------------
193
+ # Fork or clone
194
+ # -------------------------------------------------------------------------
195
+ do_fork() {
196
+ log_info "Forking ${GITHUB_REPO} → ${GH_USER}/${REPO_NAME}..."
197
+
198
+ if gh repo fork "${GITHUB_REPO}" \
199
+ --fork-name "$REPO_NAME" \
200
+ --clone=false 2>/dev/null; then
201
+ log_success "Fork created: ${GH_USER}/${REPO_NAME}"
202
+ else
203
+ log_warning "Fork may already exist — proceeding to clone."
204
+ fi
205
+
206
+ log_info "Cloning ${GH_USER}/${REPO_NAME}..."
207
+ gh repo clone "${GH_USER}/${REPO_NAME}" "$REPO_NAME" -- --depth=1 2>/dev/null || \
208
+ gh repo clone "${GH_USER}/${THEME_NAME}" "$REPO_NAME" -- --depth=1
209
+ }
210
+
211
+ do_clone() {
212
+ log_info "Cloning ${GITHUB_REPO}..."
213
+ git clone --depth=1 "${GITHUB_URL}.git" "$REPO_NAME"
214
+ }
215
+
216
+ # -------------------------------------------------------------------------
217
+ # Post-clone setup
218
+ # -------------------------------------------------------------------------
219
+ run_post_setup() {
220
+ local target_dir="$REPO_NAME"
221
+
222
+ cd "$target_dir"
223
+
224
+ # Hand off to install.sh --fork if it exists
225
+ if [[ -f "./install.sh" ]]; then
226
+ log_info "Running install.sh --fork for site personalisation..."
227
+ bash ./install.sh --fork \
228
+ --site-name "$SITE_NAME" \
229
+ --github-user "$GH_USER" \
230
+ --author "$AUTHOR" \
231
+ --email "$EMAIL" \
232
+ --non-interactive \
233
+ .
234
+ elif [[ -f "./scripts/fork-cleanup.sh" ]]; then
235
+ log_info "Running fork-cleanup.sh..."
236
+ bash ./scripts/fork-cleanup.sh \
237
+ --site-name "$SITE_NAME" \
238
+ --github-user "$GH_USER" \
239
+ --author "$AUTHOR" \
240
+ --email "$EMAIL" \
241
+ --non-interactive
242
+ else
243
+ log_warning "No install.sh or fork-cleanup.sh found. Manual setup required."
244
+ fi
245
+
246
+ cd ..
247
+ }
248
+
249
+ # -------------------------------------------------------------------------
250
+ # Main
251
+ # -------------------------------------------------------------------------
252
+ main() {
253
+ parse_args "$@"
254
+
255
+ echo
256
+ log_info "Zer0-Mistakes GitHub Setup"
257
+ echo
258
+
259
+ check_gh_cli || exit 1
260
+ check_gh_auth || exit 1
261
+ gather_values
262
+
263
+ if [[ "$CLONE_ONLY" == true ]]; then
264
+ do_clone
265
+ else
266
+ do_fork
267
+ fi
268
+
269
+ run_post_setup
270
+
271
+ echo
272
+ log_success "Setup complete!"
273
+ echo
274
+ log_info "Next steps:"
275
+ echo " cd $REPO_NAME"
276
+ echo " docker-compose up # start dev server"
277
+ echo " open http://localhost:4000 # view your site"
278
+ echo
279
+ if [[ "$CLONE_ONLY" != true ]]; then
280
+ echo " git push origin main # deploy to GitHub Pages"
281
+ fi
282
+ }
283
+
284
+ main "$@"
@@ -237,7 +237,6 @@ plugins:
237
237
  - jekyll-sitemap
238
238
  - jekyll-seo-tag
239
239
  - jekyll-paginate
240
- - jekyll-include-cache
241
240
 
242
241
  # Docker-optimized settings
243
242
  url: ""