jekyll-theme-zer0 1.18.0 → 1.19.0
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 +61 -0
- data/README.md +8 -4
- data/_data/backlog.yml +135 -2
- data/_data/features.yml +31 -10
- data/_data/hub.yml +68 -0
- data/_data/hub_index.yml +203 -0
- data/_data/navigation/hub.yml +110 -0
- data/_data/navigation/main.yml +7 -0
- data/_includes/analytics/google-analytics.html +14 -6
- data/_includes/analytics/google-tag-manager-head.html +13 -5
- data/_includes/components/ai-chat.html +143 -346
- data/_includes/core/head.html +4 -2
- data/_layouts/home.html +17 -4
- data/assets/js/ai-chat.js +853 -0
- data/scripts/content-review.rb +22 -4
- data/scripts/lib/hub.rb +208 -0
- data/scripts/provision-org-sites.rb +252 -0
- data/scripts/provision-org-sites.sh +23 -0
- data/scripts/sync-hub-metadata.rb +184 -0
- data/scripts/sync-hub-metadata.sh +22 -0
- metadata +11 -2
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# sync-hub-metadata.rb
|
|
6
|
+
# =============================================================================
|
|
7
|
+
#
|
|
8
|
+
# Refreshes the org content hub dashboard data. Content stays in the source
|
|
9
|
+
# repos (each publishes its own GitHub Pages site at
|
|
10
|
+
# https://<org>.github.io/<repo>/ — see scripts/provision-org-sites.rb); this
|
|
11
|
+
# script only gathers METADATA about them via the GitHub API:
|
|
12
|
+
#
|
|
13
|
+
# _data/hub_index.yml — per-repo stats for the /hub/ dashboard
|
|
14
|
+
# (page counts, sections, Pages status, links)
|
|
15
|
+
# _data/navigation/hub.yml — sidebar tree for the hub pages (sidebar.nav: hub)
|
|
16
|
+
#
|
|
17
|
+
# Nothing is cloned and no content is copied into this repository. Output is
|
|
18
|
+
# deterministic for unchanged sources, so the scheduled workflow only commits
|
|
19
|
+
# real changes.
|
|
20
|
+
#
|
|
21
|
+
# Usage:
|
|
22
|
+
# ruby scripts/sync-hub-metadata.rb # refresh dashboard data
|
|
23
|
+
# ruby scripts/sync-hub-metadata.rb --check # validate registry/output only (CI gate)
|
|
24
|
+
# ruby scripts/sync-hub-metadata.rb --dry-run # print planned writes, change nothing
|
|
25
|
+
#
|
|
26
|
+
# Requires the `gh` CLI (read scope). `--check` needs only the Ruby stdlib.
|
|
27
|
+
# =============================================================================
|
|
28
|
+
|
|
29
|
+
require 'optparse'
|
|
30
|
+
require 'fileutils'
|
|
31
|
+
require_relative 'lib/hub'
|
|
32
|
+
|
|
33
|
+
INDEX_FILE = File.join(Hub::ROOT, '_data', 'hub_index.yml')
|
|
34
|
+
NAV_FILE = File.join(Hub::ROOT, '_data', 'navigation', 'hub.yml')
|
|
35
|
+
|
|
36
|
+
def check_generated_output
|
|
37
|
+
errors = []
|
|
38
|
+
if File.exist?(INDEX_FILE)
|
|
39
|
+
index = Hub.load_yaml(INDEX_FILE)
|
|
40
|
+
if index.is_a?(Hash) && index['repos'].is_a?(Array)
|
|
41
|
+
index['repos'].each do |repo|
|
|
42
|
+
errors << "hub_index repo #{repo['name']}: missing site_url" unless repo['site_url'].to_s.start_with?('https://')
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
errors << '_data/hub_index.yml: missing repos list'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
errors
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Gathers everything the dashboard needs about one repo from the GitHub API:
|
|
52
|
+
# the markdown tree (for counts/sections), the Pages site status, and whether
|
|
53
|
+
# the Pages scaffold (_config.yml) has landed.
|
|
54
|
+
def inspect_repo(repo, cfg)
|
|
55
|
+
org = cfg['org']
|
|
56
|
+
excludes = (cfg['defaults'] || {})['exclude'] || []
|
|
57
|
+
|
|
58
|
+
tree = Hub.gh_api("repos/#{org}/#{repo['name']}/git/trees/#{repo['branch']}?recursive=1")
|
|
59
|
+
paths = (tree ? tree['tree'] : []).select { |e| e['type'] == 'blob' }.map { |e| e['path'] }
|
|
60
|
+
files = Hub.content_paths(paths, excludes)
|
|
61
|
+
|
|
62
|
+
pages = Hub.gh_api("repos/#{org}/#{repo['name']}/pages")
|
|
63
|
+
site_url = (pages && pages['html_url']) || "https://#{org}.github.io/#{repo['name']}/"
|
|
64
|
+
site_url += '/' unless site_url.end_with?('/')
|
|
65
|
+
|
|
66
|
+
sections = files.select { |f| f.include?('/') }.group_by { |f| f.split('/').first }.sort.map do |dir, pages_in|
|
|
67
|
+
{
|
|
68
|
+
'name' => dir,
|
|
69
|
+
'title' => section_title(org, repo, dir),
|
|
70
|
+
'url' => "#{site_url}#{dir}/",
|
|
71
|
+
'count' => pages_in.count { |f| File.basename(f, '.md').downcase != 'index' }
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
root_pages = files.reject { |f| f.include?('/') }
|
|
76
|
+
.reject { |f| %w[index readme].include?(File.basename(f, '.md').downcase) }
|
|
77
|
+
.map { |f| { 'title' => Hub.humanize(File.basename(f, '.md')), 'url' => Hub.page_url(site_url, f) } }
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
'name' => repo['name'],
|
|
81
|
+
'title' => repo['title'] || repo['name'],
|
|
82
|
+
'description' => repo['description'].to_s,
|
|
83
|
+
'url' => "https://github.com/#{org}/#{repo['name']}",
|
|
84
|
+
'site_url' => site_url,
|
|
85
|
+
'pages_enabled' => !pages.nil?,
|
|
86
|
+
'scaffolded' => paths.include?('_config.yml'),
|
|
87
|
+
'branch' => repo['branch'],
|
|
88
|
+
'pushed_at' => repo['pushed_at'].to_s,
|
|
89
|
+
'page_count' => files.size,
|
|
90
|
+
'sections' => sections,
|
|
91
|
+
'root_pages' => root_pages
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Title of a section's index page (one contents API call), falling back to a
|
|
96
|
+
# humanized directory name.
|
|
97
|
+
def section_title(org, repo, dir)
|
|
98
|
+
doc = Hub.gh_api("repos/#{org}/#{repo['name']}/contents/#{dir}/index.md?ref=#{repo['branch']}")
|
|
99
|
+
return Hub.humanize(dir) unless doc && doc['content']
|
|
100
|
+
|
|
101
|
+
Hub.title_of(Base64.decode64(doc['content']).force_encoding('utf-8'), dir)
|
|
102
|
+
rescue StandardError
|
|
103
|
+
Hub.humanize(dir)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def build_index(cfg, repos)
|
|
107
|
+
{
|
|
108
|
+
'org' => cfg['org'],
|
|
109
|
+
'totals' => { 'repos' => repos.size, 'pages' => repos.sum { |r| r['page_count'] } },
|
|
110
|
+
'repos' => repos
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def build_nav(repos)
|
|
115
|
+
items = [{ 'title' => 'Hub Dashboard', 'icon' => 'bi-grid-1x2', 'url' => '/hub/' }]
|
|
116
|
+
repos.each do |repo|
|
|
117
|
+
children = [{ 'title' => 'Site Home', 'url' => repo['site_url'] }]
|
|
118
|
+
repo['root_pages'].each { |p| children << { 'title' => p['title'], 'url' => p['url'] } }
|
|
119
|
+
repo['sections'].each { |s| children << { 'title' => s['title'], 'url' => s['url'] } }
|
|
120
|
+
items << {
|
|
121
|
+
'title' => repo['title'],
|
|
122
|
+
'icon' => 'bi-journal-richtext',
|
|
123
|
+
'url' => repo['site_url'],
|
|
124
|
+
'children' => children
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
items
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def write_generated_yaml(path, data, dry_run:)
|
|
131
|
+
content = Hub::GENERATED_HEADER + data.to_yaml.sub(/\A---\n/, '')
|
|
132
|
+
if File.exist?(path) && File.read(path, encoding: 'utf-8') == content
|
|
133
|
+
Hub.log_info "unchanged: #{path.sub("#{Hub::ROOT}/", '')}"
|
|
134
|
+
elsif dry_run
|
|
135
|
+
Hub.log_info "DRY: would write #{path.sub("#{Hub::ROOT}/", '')}"
|
|
136
|
+
else
|
|
137
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
138
|
+
File.write(path, content)
|
|
139
|
+
Hub.log_info "wrote #{path.sub("#{Hub::ROOT}/", '')}"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# ---------------------------------------------------------------------------
|
|
144
|
+
# Main
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
options = { mode: :sync, dry_run: false }
|
|
148
|
+
OptionParser.new do |opts|
|
|
149
|
+
opts.banner = 'Usage: ruby scripts/sync-hub-metadata.rb [--check|--dry-run]'
|
|
150
|
+
opts.on('--check', 'validate registry and generated data, no writes') { options[:mode] = :check }
|
|
151
|
+
opts.on('--dry-run', 'print planned actions without writing') { options[:dry_run] = true }
|
|
152
|
+
end.parse!
|
|
153
|
+
|
|
154
|
+
cfg = Hub.load_registry
|
|
155
|
+
errors = Hub.validate_registry(cfg)
|
|
156
|
+
errors += check_generated_output if options[:mode] == :check && errors.empty?
|
|
157
|
+
|
|
158
|
+
unless errors.empty?
|
|
159
|
+
errors.each { |e| Hub.log_error(e) }
|
|
160
|
+
exit 1
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
if options[:mode] == :check
|
|
164
|
+
Hub.log_info 'hub registry and generated data are valid'
|
|
165
|
+
exit 0
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
repos = Hub.discover_repos(cfg)
|
|
169
|
+
if repos.empty?
|
|
170
|
+
Hub.log_warn 'no repos found (check org and exclude_repos)'
|
|
171
|
+
exit 0
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
Hub.log_info "Inspecting #{repos.size} repo(s) in #{cfg['org']}: #{repos.map { |r| r['name'] }.join(', ')}"
|
|
175
|
+
inspected = repos.map do |repo|
|
|
176
|
+
info = inspect_repo(repo, cfg)
|
|
177
|
+
state = info['pages_enabled'] ? "live at #{info['site_url']}" : 'Pages NOT enabled'
|
|
178
|
+
Hub.log_info " #{info['name']}: #{info['page_count']} pages, #{info['sections'].size} sections — #{state}"
|
|
179
|
+
info
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
write_generated_yaml(INDEX_FILE, build_index(cfg, inspected), dry_run: options[:dry_run])
|
|
183
|
+
write_generated_yaml(NAV_FILE, build_nav(inspected), dry_run: options[:dry_run])
|
|
184
|
+
Hub.log_info 'Hub metadata refresh complete'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# sync-hub-metadata.sh
|
|
4
|
+
# =============================================================================
|
|
5
|
+
#
|
|
6
|
+
# Thin wrapper around scripts/sync-hub-metadata.rb.
|
|
7
|
+
#
|
|
8
|
+
# Refreshes the org content hub dashboard data (_data/hub_index.yml and
|
|
9
|
+
# _data/navigation/hub.yml) from the GitHub API. Content stays in the source
|
|
10
|
+
# repos — nothing is cloned or copied here.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# ./scripts/sync-hub-metadata.sh # refresh dashboard data
|
|
14
|
+
# ./scripts/sync-hub-metadata.sh --check # validate registry/output only (CI/PR gate)
|
|
15
|
+
# ./scripts/sync-hub-metadata.sh --dry-run # print planned actions only
|
|
16
|
+
#
|
|
17
|
+
# =============================================================================
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
|
+
exec ruby "${SCRIPT_DIR}/sync-hub-metadata.rb" "$@"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-theme-zer0
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Amr Abdel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jekyll
|
|
@@ -86,12 +86,15 @@ files:
|
|
|
86
86
|
- _data/generate_statistics.sh
|
|
87
87
|
- _data/github-actions-example.yml
|
|
88
88
|
- _data/glossary.yml
|
|
89
|
+
- _data/hub.yml
|
|
90
|
+
- _data/hub_index.yml
|
|
89
91
|
- _data/landing.yml
|
|
90
92
|
- _data/navigation/README.md
|
|
91
93
|
- _data/navigation/about.yml
|
|
92
94
|
- _data/navigation/admin.yml
|
|
93
95
|
- _data/navigation/docs.yml
|
|
94
96
|
- _data/navigation/home.yml
|
|
97
|
+
- _data/navigation/hub.yml
|
|
95
98
|
- _data/navigation/main.yml
|
|
96
99
|
- _data/navigation/posts.yml
|
|
97
100
|
- _data/navigation/quickstart.yml
|
|
@@ -274,6 +277,7 @@ files:
|
|
|
274
277
|
- assets/data/notebooks/survey_responses.csv
|
|
275
278
|
- assets/data/notebooks/weather_data.csv
|
|
276
279
|
- assets/data/wiki-index.json
|
|
280
|
+
- assets/js/ai-chat.js
|
|
277
281
|
- assets/js/auto-hide-nav.js
|
|
278
282
|
- assets/js/back-to-top.js
|
|
279
283
|
- assets/js/background-customizer.js
|
|
@@ -442,6 +446,7 @@ files:
|
|
|
442
446
|
- scripts/lib/frontmatter.sh
|
|
443
447
|
- scripts/lib/gem.sh
|
|
444
448
|
- scripts/lib/git.sh
|
|
449
|
+
- scripts/lib/hub.rb
|
|
445
450
|
- scripts/lib/install/README.md
|
|
446
451
|
- scripts/lib/install/agents.sh
|
|
447
452
|
- scripts/lib/install/ai/diagnose.sh
|
|
@@ -475,10 +480,14 @@ files:
|
|
|
475
480
|
- scripts/platform/setup-macos.sh
|
|
476
481
|
- scripts/platform/setup-wsl.sh
|
|
477
482
|
- scripts/post-template-setup.sh
|
|
483
|
+
- scripts/provision-org-sites.rb
|
|
484
|
+
- scripts/provision-org-sites.sh
|
|
478
485
|
- scripts/release
|
|
479
486
|
- scripts/setup.sh
|
|
480
487
|
- scripts/sync-backlog.rb
|
|
481
488
|
- scripts/sync-backlog.sh
|
|
489
|
+
- scripts/sync-hub-metadata.rb
|
|
490
|
+
- scripts/sync-hub-metadata.sh
|
|
482
491
|
- scripts/test-auto-version.sh
|
|
483
492
|
- scripts/test-mermaid.sh
|
|
484
493
|
- scripts/test-notebook-conversion.sh
|