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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +236 -0
- data/README.md +66 -19
- data/_data/navigation/admin.yml +53 -0
- data/_data/theme_backgrounds.yml +121 -0
- data/_includes/components/admin-tabs.html +59 -0
- data/_includes/components/analytics-dashboard.html +232 -0
- data/_includes/components/background-customizer.html +159 -0
- data/_includes/components/background-settings.html +137 -0
- data/_includes/components/collection-manager.html +151 -0
- data/_includes/components/component-showcase.html +452 -0
- data/_includes/components/config-editor.html +207 -0
- data/_includes/components/config-viewer.html +479 -0
- data/_includes/components/env-dashboard.html +154 -0
- data/_includes/components/feature-card.html +94 -0
- data/_includes/components/info-section.html +172 -149
- data/_includes/components/js-cdn.html +4 -1
- data/_includes/components/nav-editor.html +99 -0
- data/_includes/components/setup-banner.html +28 -0
- data/_includes/components/setup-check.html +53 -0
- data/_includes/components/svg-background.html +42 -0
- data/_includes/components/theme-customizer.html +46 -0
- data/_includes/content/seo.html +68 -135
- data/_includes/core/footer.html +1 -1
- data/_includes/core/head.html +3 -2
- data/_includes/core/header.html +14 -7
- data/_includes/landing/landing-install-cards.html +18 -7
- data/_includes/navigation/admin-nav.html +95 -0
- data/_includes/navigation/navbar.html +43 -5
- data/_includes/navigation/sidebar-left.html +1 -1
- data/_includes/setup/wizard.html +330 -0
- data/_layouts/admin.html +166 -0
- data/_layouts/landing.html +23 -9
- data/_layouts/root.html +12 -6
- data/_layouts/setup.html +73 -0
- data/_plugins/preview_image_generator.rb +26 -12
- data/_sass/core/_navbar.scss +2 -2
- data/_sass/custom.scss +28 -6
- data/_sass/theme/_background-mixins.scss +95 -0
- data/_sass/theme/_backgrounds.scss +156 -0
- data/_sass/theme/_color-modes.scss +2 -1
- data/assets/backgrounds/gradients/air.svg +15 -0
- data/assets/backgrounds/gradients/aqua.svg +15 -0
- data/assets/backgrounds/gradients/contrast.svg +15 -0
- data/assets/backgrounds/gradients/dark.svg +15 -0
- data/assets/backgrounds/gradients/dirt.svg +15 -0
- data/assets/backgrounds/gradients/mint.svg +15 -0
- data/assets/backgrounds/gradients/neon.svg +15 -0
- data/assets/backgrounds/gradients/plum.svg +15 -0
- data/assets/backgrounds/gradients/sunrise.svg +15 -0
- data/assets/backgrounds/noise/air.svg +8 -0
- data/assets/backgrounds/noise/aqua.svg +8 -0
- data/assets/backgrounds/noise/contrast.svg +8 -0
- data/assets/backgrounds/noise/dark.svg +8 -0
- data/assets/backgrounds/noise/dirt.svg +8 -0
- data/assets/backgrounds/noise/mint.svg +8 -0
- data/assets/backgrounds/noise/neon.svg +8 -0
- data/assets/backgrounds/noise/plum.svg +8 -0
- data/assets/backgrounds/noise/sunrise.svg +8 -0
- data/assets/backgrounds/patterns/air.svg +7 -0
- data/assets/backgrounds/patterns/aqua.svg +7 -0
- data/assets/backgrounds/patterns/contrast.svg +4 -0
- data/assets/backgrounds/patterns/dark.svg +5 -0
- data/assets/backgrounds/patterns/dirt.svg +5 -0
- data/assets/backgrounds/patterns/mint.svg +6 -0
- data/assets/backgrounds/patterns/neon.svg +6 -0
- data/assets/backgrounds/patterns/plum.svg +6 -0
- data/assets/backgrounds/patterns/sunrise.svg +5 -0
- data/assets/js/background-customizer.js +73 -0
- data/assets/js/code-copy.js +18 -47
- data/assets/js/config-utility.js +307 -0
- data/assets/js/nav-editor.js +39 -0
- data/assets/js/palette-generator.js +415 -0
- data/assets/js/search-modal.js +31 -11
- data/assets/js/setup-wizard.js +306 -0
- data/assets/js/skin-editor.js +645 -0
- data/assets/js/theme-customizer.js +102 -0
- data/assets/js/ui-enhancements.js +15 -24
- data/assets/vendor/bootstrap/css/bootstrap.min.css +1 -0
- data/assets/vendor/bootstrap/js/bootstrap.bundle.min.js +1 -0
- data/scripts/README.md +45 -0
- data/scripts/features/generate-preview-images +297 -7
- data/scripts/features/install-preview-generator +51 -33
- data/scripts/fork-cleanup.sh +92 -19
- data/scripts/github-setup.sh +284 -0
- data/scripts/init_setup.sh +0 -1
- data/scripts/lib/frontmatter.sh +543 -0
- data/scripts/lib/migrate.sh +265 -0
- data/scripts/lib/preview_generator.py +607 -32
- data/scripts/lint-pages +505 -0
- data/scripts/migrate.sh +201 -0
- data/scripts/platform/setup-linux.sh +244 -0
- data/scripts/platform/setup-macos.sh +187 -0
- data/scripts/platform/setup-wsl.sh +196 -0
- 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
|
-
|
|
44
|
-
"scripts/generate-preview-images
|
|
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
|
-
|
|
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
|
-
#
|
|
163
|
-
|
|
164
|
-
local
|
|
165
|
-
local
|
|
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
|
-
|
|
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 "$
|
|
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 "$
|
|
181
|
-
warn "File exists, skipping: $
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
199
|
+
step "Copying: $source_path → $dest_path"
|
|
200
|
+
cp "$local_src" "$dest_path"
|
|
194
201
|
else
|
|
195
|
-
|
|
196
|
-
|
|
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
|
|
463
|
-
for
|
|
464
|
-
|
|
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
|
data/scripts/fork-cleanup.sh
CHANGED
|
@@ -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
|
-
#
|
|
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:
|
|
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
|
-
#
|
|
359
|
-
#
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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 "$@"
|