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.
@@ -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.18.0
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-13 00:00:00.000000000 Z
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