jekyll-theme-zer0 0.22.20 → 0.22.22
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 +74 -4
- data/README.md +325 -40
- data/_data/README.md +1 -0
- data/_data/roadmap.yml +215 -0
- data/scripts/bin/install +717 -0
- data/scripts/bin/test +45 -2
- data/scripts/generate-roadmap.rb +200 -0
- data/scripts/generate-roadmap.sh +21 -0
- data/scripts/lib/install/README.md +63 -0
- data/scripts/lib/install/agents.sh +166 -0
- data/scripts/lib/install/ai/diagnose.sh +199 -0
- data/scripts/lib/install/ai/openai.sh +233 -0
- data/scripts/lib/install/ai/suggest.sh +182 -0
- data/scripts/lib/install/ai/wizard.sh +160 -0
- data/scripts/lib/install/config.sh +56 -0
- data/scripts/lib/install/deploy/README.md +52 -0
- data/scripts/lib/install/deploy/azure-swa.sh +50 -0
- data/scripts/lib/install/deploy/docker-prod.sh +71 -0
- data/scripts/lib/install/deploy/github-pages.sh +44 -0
- data/scripts/lib/install/deploy/registry.sh +190 -0
- data/scripts/lib/install/doctor.sh +301 -0
- data/scripts/lib/install/fs.sh +52 -0
- data/scripts/lib/install/logging.sh +33 -0
- data/scripts/lib/install/pages.sh +255 -0
- data/scripts/lib/install/platform.sh +71 -0
- data/scripts/lib/install/profile.sh +113 -0
- data/scripts/lib/install/template.sh +137 -0
- data/scripts/lib/install/upgrade.sh +184 -0
- data/scripts/lib/install/wizard_interactive.sh +189 -0
- metadata +27 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =========================================================================
|
|
3
|
+
# scripts/lib/install/platform.sh
|
|
4
|
+
# =========================================================================
|
|
5
|
+
# Platform / Ruby detection helpers used by install.sh.
|
|
6
|
+
#
|
|
7
|
+
# Bash 3.2-compatible — no `declare -A`, no `=~` capture groups.
|
|
8
|
+
#
|
|
9
|
+
# Functions exported:
|
|
10
|
+
# detect_os -> Darwin | Linux | CYGWIN | MINGW | unknown
|
|
11
|
+
# detect_ruby_version -> e.g. "2.6.8" or "none"
|
|
12
|
+
# ruby_version_lt_27 -> exit 0 when ruby < 2.7
|
|
13
|
+
# needs_macos_gemfile -> exit 0 when macOS + ruby < 2.7
|
|
14
|
+
# detect_platform -> auto | wsl | macos | linux | unknown
|
|
15
|
+
# =========================================================================
|
|
16
|
+
|
|
17
|
+
# Returns: Darwin | Linux | CYGWIN | MINGW | unknown
|
|
18
|
+
detect_os() {
|
|
19
|
+
uname -s 2>/dev/null || echo "unknown"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# Returns the ruby version string (e.g. "2.6.8"), or "none" if ruby is absent.
|
|
23
|
+
detect_ruby_version() {
|
|
24
|
+
if ! command -v ruby >/dev/null 2>&1; then
|
|
25
|
+
echo "none"
|
|
26
|
+
return
|
|
27
|
+
fi
|
|
28
|
+
# ruby --version prints: ruby 2.6.8p205 (2021-07-07 ...) [platform]
|
|
29
|
+
# We want "2.6.8" — strip the trailing pNNN patch indicator via sed.
|
|
30
|
+
ruby --version 2>/dev/null | awk '{print $2}' | sed 's/p[0-9]*//' | sed 's/-.*//' | tr -d '\r'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Returns 0 (true) if the current Ruby version is < 2.7.0, 1 (false) otherwise.
|
|
34
|
+
# Uses awk so arithmetic is safe even with partial version strings.
|
|
35
|
+
ruby_version_lt_27() {
|
|
36
|
+
local ver
|
|
37
|
+
ver=$(detect_ruby_version)
|
|
38
|
+
[ "$ver" = "none" ] && return 1 # no ruby → don't apply macOS caps
|
|
39
|
+
awk -v ver="$ver" 'BEGIN {
|
|
40
|
+
n = split(ver, a, ".")
|
|
41
|
+
if (a[1]+0 == 2 && a[2]+0 < 7) exit 0
|
|
42
|
+
exit 1
|
|
43
|
+
}'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Returns 0 (true) when running on macOS with system Ruby < 2.7.
|
|
47
|
+
# This is the condition that triggers use of Gemfile.macos.template.
|
|
48
|
+
needs_macos_gemfile() {
|
|
49
|
+
local os
|
|
50
|
+
os=$(detect_os)
|
|
51
|
+
[ "$os" = "Darwin" ] && ruby_version_lt_27
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Detect runtime platform. Honours $PLATFORM if explicitly set.
|
|
55
|
+
# Returns: macos | linux | wsl | unknown
|
|
56
|
+
detect_platform() {
|
|
57
|
+
if [[ "${PLATFORM:-auto}" != "auto" ]]; then
|
|
58
|
+
echo "$PLATFORM"
|
|
59
|
+
return
|
|
60
|
+
fi
|
|
61
|
+
# WSL detection (check before generic Linux)
|
|
62
|
+
if grep -qiE '(microsoft|wsl)' /proc/version 2>/dev/null; then
|
|
63
|
+
echo "wsl"
|
|
64
|
+
elif [[ "$(uname -s)" == "Darwin" ]]; then
|
|
65
|
+
echo "macos"
|
|
66
|
+
elif [[ "$(uname -s)" == "Linux" ]]; then
|
|
67
|
+
echo "linux"
|
|
68
|
+
else
|
|
69
|
+
echo "unknown"
|
|
70
|
+
fi
|
|
71
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/lib/install/profile.sh
|
|
3
|
+
#
|
|
4
|
+
# Minimal pure-bash YAML reader for templates/profiles/*.yml.
|
|
5
|
+
# Bash 3.2 compatible. Schema is intentionally simple (scalar key/value
|
|
6
|
+
# + flat string lists) so we don't need yq/python.
|
|
7
|
+
#
|
|
8
|
+
# Public API:
|
|
9
|
+
# profiles_dir <REPO_ROOT> — absolute path to profiles dir
|
|
10
|
+
# list_profile_names <REPO_ROOT> — newline-separated profile slugs
|
|
11
|
+
# profile_path <REPO_ROOT> <name> — absolute path to a profile yml
|
|
12
|
+
# profile_get_scalar <file> <key> — print a scalar value or empty
|
|
13
|
+
# profile_get_list <file> <key> — print list items, one per line
|
|
14
|
+
# profile_print_summary <file> — pretty-print full profile
|
|
15
|
+
|
|
16
|
+
profiles_dir() {
|
|
17
|
+
local repo_root="$1"
|
|
18
|
+
echo "$repo_root/templates/profiles"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
list_profile_names() {
|
|
22
|
+
local repo_root="$1"
|
|
23
|
+
local dir
|
|
24
|
+
dir="$(profiles_dir "$repo_root")"
|
|
25
|
+
[ -d "$dir" ] || return 1
|
|
26
|
+
# List *.yml stems, sorted, excluding any leading-dot files
|
|
27
|
+
( cd "$dir" && for f in *.yml; do
|
|
28
|
+
[ -e "$f" ] || continue
|
|
29
|
+
echo "${f%.yml}"
|
|
30
|
+
done ) | sort
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
profile_path() {
|
|
34
|
+
local repo_root="$1" name="$2"
|
|
35
|
+
local p
|
|
36
|
+
p="$(profiles_dir "$repo_root")/${name}.yml"
|
|
37
|
+
[ -f "$p" ] || return 1
|
|
38
|
+
echo "$p"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Extract a scalar value: lines like `key: value`. Strips surrounding
|
|
42
|
+
# whitespace and quotes. Ignores list lines (starting with `-`).
|
|
43
|
+
profile_get_scalar() {
|
|
44
|
+
local file="$1" key="$2"
|
|
45
|
+
[ -f "$file" ] || return 1
|
|
46
|
+
awk -v k="$key" '
|
|
47
|
+
# match " key: value" or "key: value" at top level (no leading dash)
|
|
48
|
+
$0 ~ "^[[:space:]]*" k "[[:space:]]*:" {
|
|
49
|
+
sub("^[[:space:]]*" k "[[:space:]]*:[[:space:]]*", "")
|
|
50
|
+
# strip trailing comments
|
|
51
|
+
sub(/[[:space:]]+#.*$/, "")
|
|
52
|
+
# strip surrounding quotes
|
|
53
|
+
gsub(/^["'\'']|["'\'']$/, "")
|
|
54
|
+
print
|
|
55
|
+
exit
|
|
56
|
+
}
|
|
57
|
+
' "$file"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Extract a flat list under a key. Returns nothing for `key: []` or missing.
|
|
61
|
+
profile_get_list() {
|
|
62
|
+
local file="$1" key="$2"
|
|
63
|
+
[ -f "$file" ] || return 1
|
|
64
|
+
awk -v k="$key" '
|
|
65
|
+
BEGIN { inblock = 0 }
|
|
66
|
+
# Start: "key:" line with nothing (or just a comment) after it
|
|
67
|
+
$0 ~ "^[[:space:]]*" k "[[:space:]]*:[[:space:]]*(#.*)?$" {
|
|
68
|
+
inblock = 1
|
|
69
|
+
next
|
|
70
|
+
}
|
|
71
|
+
inblock {
|
|
72
|
+
# End block on next non-list, non-blank, non-indented line
|
|
73
|
+
if ($0 ~ /^[[:space:]]*-[[:space:]]+/) {
|
|
74
|
+
line = $0
|
|
75
|
+
sub(/^[[:space:]]*-[[:space:]]+/, "", line)
|
|
76
|
+
sub(/[[:space:]]+#.*$/, "", line)
|
|
77
|
+
gsub(/^["'\'']|["'\'']$/, "", line)
|
|
78
|
+
print line
|
|
79
|
+
next
|
|
80
|
+
}
|
|
81
|
+
if ($0 ~ /^[[:space:]]*#/) next
|
|
82
|
+
if ($0 ~ /^[[:space:]]*$/) next
|
|
83
|
+
inblock = 0
|
|
84
|
+
}
|
|
85
|
+
' "$file"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
profile_print_summary() {
|
|
89
|
+
local file="$1"
|
|
90
|
+
[ -f "$file" ] || { echo "(profile file not found: $file)" >&2; return 1; }
|
|
91
|
+
local name display desc legacy rec
|
|
92
|
+
name="$(profile_get_scalar "$file" name)"
|
|
93
|
+
display="$(profile_get_scalar "$file" display_name)"
|
|
94
|
+
desc="$(profile_get_scalar "$file" description)"
|
|
95
|
+
legacy="$(profile_get_scalar "$file" legacy_flag)"
|
|
96
|
+
rec="$(profile_get_scalar "$file" recommended_for)"
|
|
97
|
+
printf ' %-9s %s\n' "$name" "$display"
|
|
98
|
+
[ -n "$desc" ] && printf ' %s\n' "$desc"
|
|
99
|
+
[ -n "$legacy" ] && printf ' → install.sh %s\n' "$legacy"
|
|
100
|
+
[ -n "$rec" ] && printf ' For: %s\n' "$rec"
|
|
101
|
+
local includes excludes
|
|
102
|
+
includes="$(profile_get_list "$file" includes)"
|
|
103
|
+
excludes="$(profile_get_list "$file" excludes)"
|
|
104
|
+
if [ -n "$includes" ]; then
|
|
105
|
+
printf ' Includes:\n'
|
|
106
|
+
echo "$includes" | sed 's/^/ - /'
|
|
107
|
+
fi
|
|
108
|
+
if [ -n "$excludes" ]; then
|
|
109
|
+
printf ' Excludes:\n'
|
|
110
|
+
echo "$excludes" | sed 's/^/ - /'
|
|
111
|
+
fi
|
|
112
|
+
echo
|
|
113
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =========================================================================
|
|
3
|
+
# scripts/lib/install/template.sh
|
|
4
|
+
# =========================================================================
|
|
5
|
+
# Template rendering for install.sh.
|
|
6
|
+
#
|
|
7
|
+
# Functions exported:
|
|
8
|
+
# render_template TEMPLATE_FILE [OUTPUT_FILE]
|
|
9
|
+
# Replace {{VAR_NAME}} placeholders. If OUTPUT_FILE omitted, writes
|
|
10
|
+
# to stdout.
|
|
11
|
+
#
|
|
12
|
+
# create_from_template TEMPLATE_REL OUTPUT_FILE [FALLBACK_CONTENT]
|
|
13
|
+
# Resolution order:
|
|
14
|
+
# 1. Local templates ($TEMPLATES_DIR/$TEMPLATE_REL)
|
|
15
|
+
# 2. Remote fetch from $GITHUB_RAW_URL/templates/$TEMPLATE_REL
|
|
16
|
+
# (only when REMOTE_INSTALL=true)
|
|
17
|
+
# 3. FALLBACK_CONTENT (literal string)
|
|
18
|
+
# Existing OUTPUT_FILE is preserved (skipped with a warning).
|
|
19
|
+
#
|
|
20
|
+
# templates_available
|
|
21
|
+
# Returns 0 when $TEMPLATES_DIR is set and points to an existing dir.
|
|
22
|
+
#
|
|
23
|
+
# Required globals (provided by install.sh / install.conf):
|
|
24
|
+
# THEME_NAME, THEME_GEM_NAME, THEME_DISPLAY_NAME,
|
|
25
|
+
# GITHUB_USER, GITHUB_REPO, GITHUB_URL, GITHUB_RAW_URL,
|
|
26
|
+
# DEFAULT_PORT, DEFAULT_URL,
|
|
27
|
+
# JEKYLL_VERSION, FFI_VERSION, WEBRICK_VERSION, COMMONMARKER_VERSION,
|
|
28
|
+
# GITHUB_PAGES_MAX_VERSION, COMMONMARKER_MACOS_VERSION,
|
|
29
|
+
# RUBY_MIN_VERSION_MACOS, INSTALL_MODE, REMOTE_INSTALL, TEMPLATES_DIR
|
|
30
|
+
#
|
|
31
|
+
# Optional globals (used when set):
|
|
32
|
+
# FORK_GITHUB_USER, FORK_SITE_NAME, FORK_AUTHOR, FORK_EMAIL,
|
|
33
|
+
# SITE_TITLE, SITE_DESCRIPTION, SITE_AUTHOR, SITE_EMAIL,
|
|
34
|
+
# REPOSITORY_NAME
|
|
35
|
+
# =========================================================================
|
|
36
|
+
|
|
37
|
+
# Render a template file, replacing {{VAR_NAME}} placeholders.
|
|
38
|
+
render_template() {
|
|
39
|
+
local template_file="$1"
|
|
40
|
+
local output_file="${2:-}"
|
|
41
|
+
|
|
42
|
+
if [[ ! -f "$template_file" ]]; then
|
|
43
|
+
return 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
local content
|
|
47
|
+
content=$(cat "$template_file")
|
|
48
|
+
|
|
49
|
+
# Replace all known placeholders. Order matches the original install.sh
|
|
50
|
+
# implementation to guarantee identical output.
|
|
51
|
+
content=$(echo "$content" | sed \
|
|
52
|
+
-e "s|{{THEME_NAME}}|${THEME_NAME}|g" \
|
|
53
|
+
-e "s|{{THEME_GEM_NAME}}|${THEME_GEM_NAME}|g" \
|
|
54
|
+
-e "s|{{THEME_DISPLAY_NAME}}|${THEME_DISPLAY_NAME}|g" \
|
|
55
|
+
-e "s|{{GITHUB_USER}}|${FORK_GITHUB_USER:-$GITHUB_USER}|g" \
|
|
56
|
+
-e "s|{{GITHUB_REPO}}|${GITHUB_REPO}|g" \
|
|
57
|
+
-e "s|{{GITHUB_URL}}|${GITHUB_URL}|g" \
|
|
58
|
+
-e "s|{{GITHUB_RAW_URL}}|${GITHUB_RAW_URL}|g" \
|
|
59
|
+
-e "s|{{DEFAULT_PORT}}|${DEFAULT_PORT}|g" \
|
|
60
|
+
-e "s|{{DEFAULT_URL}}|${DEFAULT_URL}|g" \
|
|
61
|
+
-e "s|{{JEKYLL_VERSION}}|${JEKYLL_VERSION}|g" \
|
|
62
|
+
-e "s|{{FFI_VERSION}}|${FFI_VERSION}|g" \
|
|
63
|
+
-e "s|{{WEBRICK_VERSION}}|${WEBRICK_VERSION}|g" \
|
|
64
|
+
-e "s|{{COMMONMARKER_VERSION}}|${COMMONMARKER_VERSION}|g" \
|
|
65
|
+
-e "s|{{GITHUB_PAGES_MAX_VERSION}}|${GITHUB_PAGES_MAX_VERSION:-232}|g" \
|
|
66
|
+
-e "s|{{COMMONMARKER_MACOS_VERSION}}|${COMMONMARKER_MACOS_VERSION:-~> 0.23}|g" \
|
|
67
|
+
-e "s|{{RUBY_MIN_VERSION_MACOS}}|${RUBY_MIN_VERSION_MACOS:-2.6.0}|g" \
|
|
68
|
+
-e "s|{{SITE_TITLE}}|${FORK_SITE_NAME:-${SITE_TITLE:-My Jekyll Site}}|g" \
|
|
69
|
+
-e "s|{{SITE_DESCRIPTION}}|${SITE_DESCRIPTION:-A Jekyll site built with ${THEME_NAME}}|g" \
|
|
70
|
+
-e "s|{{SITE_AUTHOR}}|${FORK_AUTHOR:-${SITE_AUTHOR:-Site Author}}|g" \
|
|
71
|
+
-e "s|{{SITE_EMAIL}}|${FORK_EMAIL:-${SITE_EMAIL:-your@email.com}}|g" \
|
|
72
|
+
-e "s|{{CURRENT_DATE}}|$(date +%Y-%m-%d)|g" \
|
|
73
|
+
-e "s|{{CURRENT_YEAR}}|$(date +%Y)|g" \
|
|
74
|
+
-e "s|{{REPOSITORY_NAME}}|${REPOSITORY_NAME:-$THEME_NAME}|g" \
|
|
75
|
+
-e "s|{{RAW_GITHUB_URL}}|${GITHUB_RAW_URL}|g" \
|
|
76
|
+
-e "s|{{FORK_GITHUB_USER}}|${FORK_GITHUB_USER:-${GITHUB_USER}}|g" \
|
|
77
|
+
-e "s|{{INSTALL_MODE}}|${INSTALL_MODE:-full}|g" \
|
|
78
|
+
-e "s|{{GITHUB_PAGES_URL}}|https://${FORK_GITHUB_USER:-${GITHUB_USER}}.github.io/${REPOSITORY_NAME:-$THEME_NAME}|g")
|
|
79
|
+
|
|
80
|
+
if [[ -n "$output_file" ]]; then
|
|
81
|
+
mkdir -p "$(dirname "$output_file")"
|
|
82
|
+
echo "$content" > "$output_file"
|
|
83
|
+
else
|
|
84
|
+
echo "$content"
|
|
85
|
+
fi
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Create a file from template with automatic fallback to embedded content.
|
|
89
|
+
create_from_template() {
|
|
90
|
+
local template_path="$1"
|
|
91
|
+
local output_file="$2"
|
|
92
|
+
local fallback_content="${3:-}"
|
|
93
|
+
|
|
94
|
+
# Skip if output already exists
|
|
95
|
+
if [[ -f "$output_file" ]]; then
|
|
96
|
+
log_warning "$(basename "$output_file") already exists, skipping to preserve content"
|
|
97
|
+
return 0
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Try local template first
|
|
101
|
+
if [[ -n "${TEMPLATES_DIR:-}" ]] && [[ -f "$TEMPLATES_DIR/$template_path" ]]; then
|
|
102
|
+
render_template "$TEMPLATES_DIR/$template_path" "$output_file"
|
|
103
|
+
log_info "Created $(basename "$output_file") from template"
|
|
104
|
+
return 0
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Try to fetch from GitHub for remote installs
|
|
108
|
+
if [[ "${REMOTE_INSTALL:-false}" == "true" ]]; then
|
|
109
|
+
local remote_url="${GITHUB_RAW_URL}/templates/$template_path"
|
|
110
|
+
local remote_content
|
|
111
|
+
if remote_content=$(curl -fsSL "$remote_url" 2>/dev/null); then
|
|
112
|
+
local temp_file
|
|
113
|
+
temp_file=$(mktemp)
|
|
114
|
+
echo "$remote_content" > "$temp_file"
|
|
115
|
+
render_template "$temp_file" "$output_file"
|
|
116
|
+
rm -f "$temp_file"
|
|
117
|
+
log_info "Created $(basename "$output_file") from remote template"
|
|
118
|
+
return 0
|
|
119
|
+
fi
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# Use fallback content if provided
|
|
123
|
+
if [[ -n "$fallback_content" ]]; then
|
|
124
|
+
mkdir -p "$(dirname "$output_file")"
|
|
125
|
+
echo "$fallback_content" > "$output_file"
|
|
126
|
+
log_info "Created $(basename "$output_file") from fallback"
|
|
127
|
+
return 0
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
log_warning "Could not create $(basename "$output_file") (no template or fallback)"
|
|
131
|
+
return 1
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# Check if templates are available (TEMPLATES_DIR set + directory exists).
|
|
135
|
+
templates_available() {
|
|
136
|
+
[[ -n "${TEMPLATES_DIR:-}" ]] && [[ -d "$TEMPLATES_DIR" ]]
|
|
137
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/lib/install/upgrade.sh
|
|
3
|
+
#
|
|
4
|
+
# `install upgrade` — version-aware migration over an existing site.
|
|
5
|
+
#
|
|
6
|
+
# Strategy:
|
|
7
|
+
# 1. Detect installed version from .zer0-installed (drop-marker) or
|
|
8
|
+
# _config.yml's `version:` field, or fall back to "unknown".
|
|
9
|
+
# 2. Print a diff-summary of what will change (theme files only — never
|
|
10
|
+
# touches user content under pages/, _posts/, _drafts/).
|
|
11
|
+
# 3. Re-runs the agent-files install (always safe, additive) and offers
|
|
12
|
+
# to refresh templated workflows under .github/workflows/.
|
|
13
|
+
# 4. Writes a fresh .zer0-installed marker with the new version + date.
|
|
14
|
+
#
|
|
15
|
+
# Public API:
|
|
16
|
+
# upgrade_run <target_dir> <repo_root> [--from <version>] [--force]
|
|
17
|
+
# [--dry-run] [--auto-accept]
|
|
18
|
+
#
|
|
19
|
+
# Bash 3.2-compatible. Idempotent. Never destructive without explicit --force.
|
|
20
|
+
|
|
21
|
+
# shellcheck disable=SC2034
|
|
22
|
+
UPGRADE_LIB_VERSION="1.0.0"
|
|
23
|
+
|
|
24
|
+
UPGRADE_MARKER=".zer0-installed"
|
|
25
|
+
|
|
26
|
+
# Read theme version from lib/jekyll-theme-zer0/version.rb
|
|
27
|
+
_upgrade_theme_version() {
|
|
28
|
+
local repo_root="$1"
|
|
29
|
+
local vfile="$repo_root/lib/jekyll-theme-zer0/version.rb"
|
|
30
|
+
[[ -f "$vfile" ]] || { echo "unknown"; return; }
|
|
31
|
+
grep -E 'VERSION\s*=' "$vfile" | head -n1 | sed -E 's/.*"([^"]+)".*/\1/'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Detect the previously installed version (best-effort).
|
|
35
|
+
_upgrade_detect_installed() {
|
|
36
|
+
local target_dir="$1"
|
|
37
|
+
if [[ -f "$target_dir/$UPGRADE_MARKER" ]]; then
|
|
38
|
+
grep -E '^version:' "$target_dir/$UPGRADE_MARKER" 2>/dev/null \
|
|
39
|
+
| head -n1 | sed -E 's/version:[[:space:]]*//'
|
|
40
|
+
return
|
|
41
|
+
fi
|
|
42
|
+
# Fallback: probe _config.yml for a `version:` line
|
|
43
|
+
if [[ -f "$target_dir/_config.yml" ]]; then
|
|
44
|
+
local v
|
|
45
|
+
v="$(grep -E '^version:' "$target_dir/_config.yml" 2>/dev/null \
|
|
46
|
+
| head -n1 | sed -E 's/version:[[:space:]]*//' \
|
|
47
|
+
| tr -d '"' | tr -d "'")"
|
|
48
|
+
[[ -n "$v" ]] && { echo "$v"; return; }
|
|
49
|
+
fi
|
|
50
|
+
echo "unknown"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Write/update the install marker.
|
|
54
|
+
_upgrade_write_marker() {
|
|
55
|
+
local target_dir="$1" version="$2" dry_run="$3"
|
|
56
|
+
local marker="$target_dir/$UPGRADE_MARKER"
|
|
57
|
+
local content
|
|
58
|
+
content="$(cat <<EOF
|
|
59
|
+
# zer0-mistakes install marker — do not edit manually
|
|
60
|
+
version: $version
|
|
61
|
+
upgraded_at: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
62
|
+
EOF
|
|
63
|
+
)"
|
|
64
|
+
if [[ "$dry_run" = "1" ]]; then
|
|
65
|
+
log_info "[dry-run] Would write marker: $marker"
|
|
66
|
+
return
|
|
67
|
+
fi
|
|
68
|
+
printf '%s\n' "$content" > "$marker"
|
|
69
|
+
log_success "Wrote $UPGRADE_MARKER (version: $version)"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Re-run agents install in --force mode (theme files are safe to refresh).
|
|
73
|
+
_upgrade_refresh_agents() {
|
|
74
|
+
local target_dir="$1" repo_root="$2" force="$3" dry_run="$4"
|
|
75
|
+
if ! declare -F agents_install >/dev/null 2>&1; then
|
|
76
|
+
log_warning "agents.sh not loaded — skipping agent-file refresh"
|
|
77
|
+
return 0
|
|
78
|
+
fi
|
|
79
|
+
if [[ "$dry_run" = "1" ]]; then
|
|
80
|
+
log_info "[dry-run] Would refresh agent files in $target_dir"
|
|
81
|
+
return 0
|
|
82
|
+
fi
|
|
83
|
+
local force_flag=""
|
|
84
|
+
[[ "$force" = "1" ]] && force_flag="--force"
|
|
85
|
+
log_info "Refreshing AI agent files..."
|
|
86
|
+
agents_install "$target_dir" "$repo_root" $force_flag || true
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Compare a workflow file in target_dir vs theme template — list
|
|
90
|
+
# differences so user can decide whether to merge.
|
|
91
|
+
_upgrade_check_workflows() {
|
|
92
|
+
local target_dir="$1" repo_root="$2"
|
|
93
|
+
local wf_dir="$target_dir/.github/workflows"
|
|
94
|
+
[[ -d "$wf_dir" ]] || return 0
|
|
95
|
+
local f base tpl
|
|
96
|
+
log_info "Checking .github/workflows/ for theme-managed files..."
|
|
97
|
+
local found=0
|
|
98
|
+
for f in "$wf_dir"/*.yml "$wf_dir"/*.yaml; do
|
|
99
|
+
[[ -f "$f" ]] || continue
|
|
100
|
+
base="$(basename "$f")"
|
|
101
|
+
# Look for a matching template under templates/deploy/*/
|
|
102
|
+
for tpl in "$repo_root/templates/deploy"/*/"$base.template" \
|
|
103
|
+
"$repo_root/templates/deploy"/*/"$base"; do
|
|
104
|
+
[[ -f "$tpl" ]] || continue
|
|
105
|
+
found=$((found+1))
|
|
106
|
+
if diff -q "$f" "$tpl" >/dev/null 2>&1; then
|
|
107
|
+
log_success " $base — up to date"
|
|
108
|
+
else
|
|
109
|
+
log_warning " $base — differs from theme template"
|
|
110
|
+
log_info " Compare: diff $f $tpl"
|
|
111
|
+
fi
|
|
112
|
+
break
|
|
113
|
+
done
|
|
114
|
+
done
|
|
115
|
+
[[ "$found" = "0" ]] && log_info " No theme-managed workflows detected"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Public entrypoint.
|
|
119
|
+
upgrade_run() {
|
|
120
|
+
local target_dir="$1" repo_root="$2"
|
|
121
|
+
shift 2 || true
|
|
122
|
+
|
|
123
|
+
local from_version="" force=0 dry_run=0 auto_accept=0
|
|
124
|
+
while [[ $# -gt 0 ]]; do
|
|
125
|
+
case "$1" in
|
|
126
|
+
--from) from_version="${2:-}"; shift ;;
|
|
127
|
+
-f|--force) force=1 ;;
|
|
128
|
+
-n|--dry-run) dry_run=1 ;;
|
|
129
|
+
--auto-accept) auto_accept=1 ;;
|
|
130
|
+
*) log_warning "upgrade_run: ignoring unknown flag: $1" ;;
|
|
131
|
+
esac
|
|
132
|
+
shift
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
if [[ ! -d "$target_dir" ]]; then
|
|
136
|
+
log_error "Target directory does not exist: $target_dir"
|
|
137
|
+
return 1
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
local installed_version theme_version
|
|
141
|
+
installed_version="${from_version:-$(_upgrade_detect_installed "$target_dir")}"
|
|
142
|
+
theme_version="$(_upgrade_theme_version "$repo_root")"
|
|
143
|
+
|
|
144
|
+
log_info "🔧 Upgrading site at: $target_dir"
|
|
145
|
+
log_info " From version: $installed_version"
|
|
146
|
+
log_info " To version: $theme_version"
|
|
147
|
+
[[ "$dry_run" = "1" ]] && log_warning " Mode: dry-run (no files will be changed)"
|
|
148
|
+
echo
|
|
149
|
+
|
|
150
|
+
if [[ "$installed_version" = "$theme_version" ]] && [[ "$force" != "1" ]]; then
|
|
151
|
+
log_success "Already on $theme_version. Use --force to re-run anyway."
|
|
152
|
+
return 0
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Confirmation gate (skipped when --auto-accept or --dry-run)
|
|
156
|
+
if [[ "$auto_accept" != "1" ]] && [[ "$dry_run" != "1" ]]; then
|
|
157
|
+
printf "Proceed with upgrade? [y/N] "
|
|
158
|
+
local reply
|
|
159
|
+
read -r reply || reply=""
|
|
160
|
+
case "$reply" in
|
|
161
|
+
y|Y|yes|YES) ;;
|
|
162
|
+
*) log_warning "Upgrade cancelled by user."; return 0 ;;
|
|
163
|
+
esac
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
# 1. Refresh agent files (always additive/safe)
|
|
167
|
+
_upgrade_refresh_agents "$target_dir" "$repo_root" "$force" "$dry_run"
|
|
168
|
+
echo
|
|
169
|
+
|
|
170
|
+
# 2. Check workflows (read-only — never auto-overwrite)
|
|
171
|
+
_upgrade_check_workflows "$target_dir" "$repo_root"
|
|
172
|
+
echo
|
|
173
|
+
|
|
174
|
+
# 3. Write/update marker (only if not dry-run)
|
|
175
|
+
_upgrade_write_marker "$target_dir" "$theme_version" "$dry_run"
|
|
176
|
+
echo
|
|
177
|
+
|
|
178
|
+
log_success "Upgrade complete."
|
|
179
|
+
log_info "Next steps:"
|
|
180
|
+
log_info " 1. Run 'install doctor' to verify environment"
|
|
181
|
+
log_info " 2. Review any workflow files flagged above"
|
|
182
|
+
log_info " 3. Check CHANGELOG.md in the theme repo for breaking changes"
|
|
183
|
+
return 0
|
|
184
|
+
}
|