spreen-wiki 0.1.0 → 0.2.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/README.md +8 -7
- data/exe/spreen +2 -2
- data/lib/spreen-wiki.rb +2 -2
- data/lib/spreen_wiki/application.rb +149 -0
- data/lib/spreen_wiki/cli.rb +157 -0
- data/lib/spreen_wiki/configuration.rb +215 -0
- data/lib/spreen_wiki/home.rb +100 -0
- data/lib/spreen_wiki/sidebar.rb +48 -0
- data/lib/spreen_wiki/unknown_wiki_count_list_exporter.rb +59 -0
- data/lib/spreen_wiki/unknown_wiki_list_exporter_for_llm.rb +51 -0
- data/lib/{spreen/wiki → spreen_wiki}/version.rb +2 -4
- data/lib/spreen_wiki.rb +18 -0
- data/sig/generated/spreen_wiki/application.rbs +91 -0
- data/sig/generated/spreen_wiki/cli.rbs +68 -0
- data/sig/generated/spreen_wiki/configuration.rbs +148 -0
- data/sig/generated/spreen_wiki/home.rbs +52 -0
- data/sig/generated/spreen_wiki/sidebar.rbs +29 -0
- data/sig/generated/spreen_wiki/unknown_wiki_count_list_exporter.rbs +38 -0
- data/sig/generated/spreen_wiki/unknown_wiki_list_exporter_for_llm.rbs +35 -0
- data/sig/generated/spreen_wiki/version.rbs +5 -0
- data/sig/generated/spreen_wiki.rbs +8 -0
- metadata +23 -23
- data/lib/spreen/wiki/application.rb +0 -151
- data/lib/spreen/wiki/cli.rb +0 -159
- data/lib/spreen/wiki/configuration.rb +0 -217
- data/lib/spreen/wiki/home.rb +0 -102
- data/lib/spreen/wiki/sidebar.rb +0 -50
- data/lib/spreen/wiki/unknown_wiki_count_list_exporter.rb +0 -61
- data/lib/spreen/wiki/unknown_wiki_list_exporter_for_llm.rb +0 -53
- data/lib/spreen/wiki.rb +0 -20
- data/sig/generated/spreen/wiki/application.rbs +0 -93
- data/sig/generated/spreen/wiki/cli.rbs +0 -70
- data/sig/generated/spreen/wiki/configuration.rbs +0 -150
- data/sig/generated/spreen/wiki/home.rbs +0 -54
- data/sig/generated/spreen/wiki/sidebar.rbs +0 -31
- data/sig/generated/spreen/wiki/unknown_wiki_count_list_exporter.rbs +0 -40
- data/sig/generated/spreen/wiki/unknown_wiki_list_exporter_for_llm.rbs +0 -37
- data/sig/generated/spreen/wiki/version.rbs +0 -7
- data/sig/generated/spreen/wiki.rbs +0 -10
- /data/lib/{spreen/wiki → spreen_wiki}/templates/category/english.md +0 -0
- /data/lib/{spreen/wiki → spreen_wiki}/templates/category/japanese.md +0 -0
- /data/lib/{spreen/wiki → spreen_wiki}/templates/owner/english.md +0 -0
- /data/lib/{spreen/wiki → spreen_wiki}/templates/owner/japanese.md +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e9507fb9c2097bc8c1221f21934eebeef6eac80a6e85ec217afed7a031a2db9
|
|
4
|
+
data.tar.gz: 5ce2f2ca5db1a5c9244b475d3866ca32d7463b4deb5beb4cfca270c869b1051a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f27d7c79f9e9e3fd7185471eca0b83c08ccbf441c2e03ec37ea578a944b5d1ac459bdde7af863e4332b072e090bda90698bf01b39de37b21ff2f301fce2f12c5
|
|
7
|
+
data.tar.gz: 2ce2882fb724ca7066026b5b2fbb6468f43c81d617c9cc9af4c8d56d8636b7850c39899018bc3a4ca3a1f0ea3f6d10bcf6595f3100f3d185de905f95ec9d6545
|
data/README.md
CHANGED
|
@@ -35,12 +35,12 @@ Persistent settings, custom labels and additional languages go into a [`.spreen.
|
|
|
35
35
|
## 3. Library Usage
|
|
36
36
|
|
|
37
37
|
```ruby
|
|
38
|
-
require '
|
|
38
|
+
require 'spreen_wiki'
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
SpreenWiki::Home.run(base_path: '.', organisation: 'your-org', repository: 'your-repo')
|
|
41
|
+
SpreenWiki::Sidebar.run(base_path: '.', organisation: 'your-org', repository: 'your-repo')
|
|
42
|
+
SpreenWiki::UnknownWikiCountListExporter.run(base_path: '.', output: 'report.txt')
|
|
43
|
+
SpreenWiki::UnknownWikiListExporterForLLM.run(base_path: '.', group_by: 'Category', language: 'Japanese')
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
All classes accept `base_path:`, `group_by:`, `language:`, `home_overflow:` plus the configuration keywords `organisation:`, `repository:`, `wiki_url:`, `owner_base_url:`, `template_dir:`, `excluded_dirs:`, `config_path:` (and `output:` on the exporters).
|
|
@@ -49,8 +49,9 @@ All classes accept `base_path:`, `group_by:`, `language:`, `home_overflow:` plus
|
|
|
49
49
|
|
|
50
50
|
### 4-1. Environment
|
|
51
51
|
|
|
52
|
-
- Ruby 4.0.
|
|
53
|
-
-
|
|
52
|
+
- Ruby 4.0.6
|
|
53
|
+
- Gemfile 4.0.16
|
|
54
|
+
- Bundler 4.0.16
|
|
54
55
|
|
|
55
56
|
```command
|
|
56
57
|
$ bundle install
|
data/exe/spreen
CHANGED
data/lib/spreen-wiki.rb
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# rbs_inline: enabled
|
|
3
3
|
|
|
4
4
|
# Convenience shim so that both `require 'spreen-wiki'` (the gem name) and
|
|
5
|
-
# `require '
|
|
6
|
-
require_relative '
|
|
5
|
+
# `require 'spreen_wiki'` (the conventional path) work.
|
|
6
|
+
require_relative 'spreen_wiki'
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require_relative 'configuration'
|
|
5
|
+
|
|
6
|
+
module SpreenWiki
|
|
7
|
+
# Base class for the wiki-organising commands. Loads the wiki tree under
|
|
8
|
+
# base_path, groups it by Owner or Category in either English or Japanese,
|
|
9
|
+
# and exposes the resulting maps to its subclasses. Grouping criteria,
|
|
10
|
+
# languages and labels are resolved through Configuration and can be
|
|
11
|
+
# extended via a `.spreen.yml` file.
|
|
12
|
+
class Application
|
|
13
|
+
class NotImplementedError < StandardError; end
|
|
14
|
+
|
|
15
|
+
# @rbs base_path: String
|
|
16
|
+
# @rbs group_by: String
|
|
17
|
+
# @rbs language: String
|
|
18
|
+
# @rbs home_overflow: (bool | String)
|
|
19
|
+
# @rbs **options: untyped
|
|
20
|
+
# @rbs return: untyped
|
|
21
|
+
def self.run(base_path: Dir.pwd, group_by: 'Owner', language: 'English', home_overflow: 'false', **)
|
|
22
|
+
instance = new(base_path:, group_by:, language:, home_overflow:, **)
|
|
23
|
+
instance.validate!
|
|
24
|
+
instance.run
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @rbs base_path: String
|
|
28
|
+
# @rbs group_by: String
|
|
29
|
+
# @rbs language: String
|
|
30
|
+
# @rbs home_overflow: (bool | String)
|
|
31
|
+
# @rbs config: Configuration?
|
|
32
|
+
# @rbs **options: untyped
|
|
33
|
+
# @rbs return: void
|
|
34
|
+
def initialize(base_path: Dir.pwd, group_by: 'Owner', language: 'English', home_overflow: 'false',
|
|
35
|
+
config: nil, **)
|
|
36
|
+
@base_path = base_path
|
|
37
|
+
@group_by = group_by
|
|
38
|
+
@language = language
|
|
39
|
+
@home_overflow = parse_home_overflow(home_overflow)
|
|
40
|
+
@config = config || Configuration.new(base_path:, **)
|
|
41
|
+
@path_to_home = File.join(base_path, 'Home.md')
|
|
42
|
+
@path_to_sidebar = File.join(base_path, '_Sidebar.md')
|
|
43
|
+
@paths_to_wikis = Dir[File.join(base_path, '**', '*.md')]
|
|
44
|
+
@excluded_paths = @config.excluded_dirs.flat_map { |dir| Dir[File.join(base_path, dir, '**', '*.md')] }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @rbs return: void
|
|
48
|
+
def validate!
|
|
49
|
+
raise ArgumentError, "Invalid group_by: `#{group_by}`" unless config.group_bys.include?(group_by)
|
|
50
|
+
raise ArgumentError, "Invalid language: `#{language}`" unless config.languages(group_by).include?(language)
|
|
51
|
+
|
|
52
|
+
validate_home_overflow!
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @rbs return: untyped
|
|
56
|
+
def run
|
|
57
|
+
raise NotImplementedError, 'This method must be implemented in each subclass.'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
attr_reader :base_path,
|
|
63
|
+
:group_by,
|
|
64
|
+
:language,
|
|
65
|
+
:home_overflow,
|
|
66
|
+
:config,
|
|
67
|
+
:path_to_home,
|
|
68
|
+
:path_to_sidebar,
|
|
69
|
+
:paths_to_wikis,
|
|
70
|
+
:excluded_paths
|
|
71
|
+
|
|
72
|
+
# @rbs return: void
|
|
73
|
+
def validate_home_overflow!
|
|
74
|
+
return if [true, false].include?(home_overflow)
|
|
75
|
+
|
|
76
|
+
raise ArgumentError, "Invalid home_overflow: `#{home_overflow}` must be boolean"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @rbs raw: (bool | String)
|
|
80
|
+
# @rbs return: (bool | String)
|
|
81
|
+
def parse_home_overflow(raw)
|
|
82
|
+
case raw
|
|
83
|
+
when 'true' then true
|
|
84
|
+
when 'false' then false
|
|
85
|
+
else raw
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @rbs return: Array[String]
|
|
90
|
+
def target_paths
|
|
91
|
+
@target_paths ||= paths_to_wikis.reject do |path_to_wiki|
|
|
92
|
+
path_to_wiki == path_to_home ||
|
|
93
|
+
path_to_wiki == path_to_sidebar ||
|
|
94
|
+
excluded_paths.include?(path_to_wiki)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @rbs return: Regexp
|
|
99
|
+
def target_regexp
|
|
100
|
+
@target_regexp ||= config.target_regexp(group_by)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @rbs return: String
|
|
104
|
+
def no_declaration
|
|
105
|
+
@no_declaration ||= config.no_declaration(group_by, language)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @rbs return: Hash[String, Array[String]]
|
|
109
|
+
def wiki_maps_with_namespace
|
|
110
|
+
@wiki_maps_with_namespace ||= begin
|
|
111
|
+
named = Hash.new { |hash, namespace| hash[namespace] = [] } #: Hash[String, Array[String]]
|
|
112
|
+
undeclared = Hash.new { |hash, namespace| hash[namespace] = [] } #: Hash[String, Array[String]]
|
|
113
|
+
target_paths.each { |target_path| populate_namespace(target_path, named, undeclared) }
|
|
114
|
+
named.sort.to_h.merge(undeclared)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @rbs target_path: String
|
|
119
|
+
# @rbs named: Hash[String, Array[String]]
|
|
120
|
+
# @rbs undeclared: Hash[String, Array[String]]
|
|
121
|
+
# @rbs return: void
|
|
122
|
+
def populate_namespace(target_path, named, undeclared)
|
|
123
|
+
return unless File.exist?(target_path)
|
|
124
|
+
|
|
125
|
+
namespace = namespace_for(target_path)
|
|
126
|
+
bucket = namespace == no_declaration ? undeclared : named
|
|
127
|
+
bucket[namespace] << File.basename(target_path)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @rbs target_path: String
|
|
131
|
+
# @rbs return: String
|
|
132
|
+
def namespace_for(target_path)
|
|
133
|
+
declaration = File.open(target_path, &:gets)
|
|
134
|
+
return no_declaration unless declaration&.start_with?(target_regexp)
|
|
135
|
+
|
|
136
|
+
declaration.chomp.gsub(target_regexp, '')
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @rbs return: Hash[String, Array[String]]
|
|
140
|
+
def owned_wiki_maps
|
|
141
|
+
@owned_wiki_maps ||= wiki_maps_with_namespace.select { |namespace, _| namespace.include?('@') }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# @rbs return: Hash[String, Array[String]]
|
|
145
|
+
def plain_wiki_maps
|
|
146
|
+
@plain_wiki_maps ||= wiki_maps_with_namespace.reject { |namespace, _| namespace.include?('@') }
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require 'optparse'
|
|
5
|
+
require_relative 'version'
|
|
6
|
+
require_relative 'home'
|
|
7
|
+
require_relative 'sidebar'
|
|
8
|
+
require_relative 'unknown_wiki_count_list_exporter'
|
|
9
|
+
require_relative 'unknown_wiki_list_exporter_for_llm'
|
|
10
|
+
|
|
11
|
+
module SpreenWiki
|
|
12
|
+
# Command line interface behind the `spreen` executable:
|
|
13
|
+
# `spreen <update|count-report|llm-export> [options]`.
|
|
14
|
+
class CLI
|
|
15
|
+
HELP = <<~HELP
|
|
16
|
+
Usage: spreen <command> [options]
|
|
17
|
+
|
|
18
|
+
Commands:
|
|
19
|
+
update Regenerate Home.md and _Sidebar.md from the wiki tree
|
|
20
|
+
count-report Export a count report of the unknown-namespace wikis
|
|
21
|
+
llm-export Export the unknown-namespace wiki list for an LLM
|
|
22
|
+
version Print the version
|
|
23
|
+
|
|
24
|
+
Run `spreen <command> --help` to list the command's options.
|
|
25
|
+
HELP
|
|
26
|
+
|
|
27
|
+
# Flags shared by every command, mapped to the keyword arguments of the
|
|
28
|
+
# underlying classes. --exclude and the per-command flags are wired up
|
|
29
|
+
# separately because they need a type or a fixed value.
|
|
30
|
+
COMMON_OPTIONS = {
|
|
31
|
+
'--path PATH' => [:base_path, 'Path to the wiki clone (default: current directory)'],
|
|
32
|
+
'--group-by GROUP_BY' => [:group_by, 'Grouping criteria (default: Owner)'],
|
|
33
|
+
'--language LANGUAGE' => [:language, 'Language of the labels (default: English)'],
|
|
34
|
+
'--org ORGANISATION' => [:organisation, 'GitHub organisation/user hosting the wiki'],
|
|
35
|
+
'--repo REPOSITORY' => [:repository, 'GitHub repository whose wiki is organised'],
|
|
36
|
+
'--wiki-url URL' => [:wiki_url, 'Wiki URL (overrides the one derived from --org/--repo)'],
|
|
37
|
+
'--template-dir DIR' => [:template_dir, 'Directory of <group_by>/<language>.md Home templates'],
|
|
38
|
+
'--config FILE' => [:config_path, 'Config file path (default: <path>/.spreen.yml)']
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
# @rbs argv: Array[String]
|
|
42
|
+
# @rbs return: Integer
|
|
43
|
+
def self.start(argv = ARGV)
|
|
44
|
+
new(argv).run
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @rbs argv: Array[String]
|
|
48
|
+
# @rbs return: void
|
|
49
|
+
def initialize(argv)
|
|
50
|
+
@argv = argv.dup
|
|
51
|
+
@options = { base_path: Dir.pwd, group_by: 'Owner', language: 'English', home_overflow: 'false' }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @rbs return: Integer
|
|
55
|
+
def run
|
|
56
|
+
command = argv.shift
|
|
57
|
+
case command
|
|
58
|
+
when 'update', 'count-report', 'llm-export' then execute(command)
|
|
59
|
+
when 'version', '--version', '-v' then print_version
|
|
60
|
+
when nil, 'help', '--help', '-h' then print_help(command)
|
|
61
|
+
else unknown_command(command)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
attr_reader :argv, :options
|
|
68
|
+
|
|
69
|
+
# @rbs command: String
|
|
70
|
+
# @rbs return: Integer
|
|
71
|
+
def execute(command)
|
|
72
|
+
parser(command).parse!(argv)
|
|
73
|
+
__send__(command.tr('-', '_'))
|
|
74
|
+
0
|
|
75
|
+
rescue ArgumentError, OptionParser::ParseError => e
|
|
76
|
+
warn e.message
|
|
77
|
+
1
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @rbs return: Integer
|
|
81
|
+
def print_version
|
|
82
|
+
puts VERSION
|
|
83
|
+
0
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @rbs command: String?
|
|
87
|
+
# @rbs return: Integer
|
|
88
|
+
def print_help(command)
|
|
89
|
+
puts HELP
|
|
90
|
+
command.nil? ? 1 : 0
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @rbs command: String
|
|
94
|
+
# @rbs return: Integer
|
|
95
|
+
def unknown_command(command)
|
|
96
|
+
warn "Unknown command: `#{command}`"
|
|
97
|
+
warn HELP
|
|
98
|
+
1
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# @rbs return: void
|
|
102
|
+
def update
|
|
103
|
+
wiki_url = Home.run(**options)
|
|
104
|
+
Sidebar.run(**options)
|
|
105
|
+
puts 'Updated Home.md and _Sidebar.md.'
|
|
106
|
+
puts "Check them out at '#{wiki_url}' !!" if wiki_url
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @rbs return: void
|
|
110
|
+
def count_report
|
|
111
|
+
count_list_by_namespace, path_to_export = UnknownWikiCountListExporter.run(**options)
|
|
112
|
+
puts count_list_by_namespace
|
|
113
|
+
puts "Exported the unknown wiki count report to '#{path_to_export}'."
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @rbs return: void
|
|
117
|
+
def llm_export
|
|
118
|
+
path_to_export = UnknownWikiListExporterForLLM.run(**options)
|
|
119
|
+
puts "Exported the unknown wiki list for LLM to '#{path_to_export}'."
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @rbs command: String
|
|
123
|
+
# @rbs return: OptionParser
|
|
124
|
+
def parser(command)
|
|
125
|
+
OptionParser.new("Usage: spreen #{command} [options]") do |opt|
|
|
126
|
+
add_common_options(opt)
|
|
127
|
+
add_command_options(opt, command)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @rbs opt: OptionParser
|
|
132
|
+
# @rbs return: void
|
|
133
|
+
def add_common_options(opt)
|
|
134
|
+
COMMON_OPTIONS.each do |flag, (key, description)|
|
|
135
|
+
opt.on(flag, description) { |value| options[key] = value }
|
|
136
|
+
end
|
|
137
|
+
opt.on('--exclude DIR1,DIR2', Array, 'Directories to skip while scanning') do |dirs|
|
|
138
|
+
options[:excluded_dirs] = dirs
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# @rbs opt: OptionParser
|
|
143
|
+
# @rbs command: String
|
|
144
|
+
# @rbs return: void
|
|
145
|
+
def add_command_options(opt, command)
|
|
146
|
+
if command == 'update'
|
|
147
|
+
opt.on('--overflow', 'Split Home into per-namespace pages under wikis-by-owner/') do
|
|
148
|
+
options[:home_overflow] = 'true'
|
|
149
|
+
end
|
|
150
|
+
else
|
|
151
|
+
opt.on('--output FILENAME', 'Export filename (relative to --path unless absolute)') do |filename|
|
|
152
|
+
options[:output] = filename
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
module SpreenWiki
|
|
7
|
+
# Resolves the runtime configuration for a wiki checkout. Every value is
|
|
8
|
+
# looked up with the same precedence: explicit keyword argument, then the
|
|
9
|
+
# `.spreen.yml` file under base_path (or config_path), then the
|
|
10
|
+
# ORGANISATION_NAME environment variable (organisation only), then the
|
|
11
|
+
# built-in defaults.
|
|
12
|
+
class Configuration
|
|
13
|
+
CONFIG_FILENAME = '.spreen.yml'
|
|
14
|
+
EMPTY_HASH = {}.freeze #: Hash[String, untyped]
|
|
15
|
+
DEFAULT_TEMPLATE_DIR = File.expand_path(File.join(__dir__.to_s, 'templates'))
|
|
16
|
+
DEFAULT_EXCLUDED_DIRS = %w[spreen-wiki wikis-by-owner].freeze
|
|
17
|
+
|
|
18
|
+
# Built-in labels: how the first line of a wiki page is parsed (regexp),
|
|
19
|
+
# which namespace collects pages without a declaration (no_declaration),
|
|
20
|
+
# which namespaces the count report covers (unknown_namespaces) and which
|
|
21
|
+
# namespace the LLM export targets (llm_target_namespace). A config file
|
|
22
|
+
# can override any of these, add languages or add group_by criteria.
|
|
23
|
+
DEFAULT_LABELS = {
|
|
24
|
+
'Owner' => {
|
|
25
|
+
'regexp' => '[Oo]wner:\s?',
|
|
26
|
+
'languages' => {
|
|
27
|
+
'English' => {
|
|
28
|
+
'no_declaration' => 'Unowned',
|
|
29
|
+
'unknown_namespaces' => ['Unknown Owner nor Necessity', 'Unowned but Necessary', 'Unowned'],
|
|
30
|
+
'llm_target_namespace' => 'Unknown Owner nor Necessity'
|
|
31
|
+
},
|
|
32
|
+
'Japanese' => {
|
|
33
|
+
'no_declaration' => 'Owner記名なし',
|
|
34
|
+
'unknown_namespaces' => [
|
|
35
|
+
'Ownerチームが不明だが必要なページ群',
|
|
36
|
+
'Ownerチーム・要or不要が不明なページ群',
|
|
37
|
+
'Owner記名なし'
|
|
38
|
+
],
|
|
39
|
+
'llm_target_namespace' => 'Ownerチーム・要or不要が不明なページ群'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
'Category' => {
|
|
44
|
+
'regexp' => '[Cc]ategory:\s?',
|
|
45
|
+
'languages' => {
|
|
46
|
+
'English' => {
|
|
47
|
+
'no_declaration' => 'Uncategorised',
|
|
48
|
+
'unknown_namespaces' => ['Uncategorised'],
|
|
49
|
+
'llm_target_namespace' => 'Uncategorised'
|
|
50
|
+
},
|
|
51
|
+
'Japanese' => {
|
|
52
|
+
'no_declaration' => 'Category記載なし',
|
|
53
|
+
'unknown_namespaces' => ['Category記載なし'],
|
|
54
|
+
'llm_target_namespace' => 'Category記載なし'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}.freeze
|
|
59
|
+
|
|
60
|
+
attr_reader :organisation, :repository, :wiki_url, :owner_base_url, :excluded_dirs, :template_dir
|
|
61
|
+
|
|
62
|
+
# @rbs!
|
|
63
|
+
# @organisation: String?
|
|
64
|
+
# @repository: String?
|
|
65
|
+
# @wiki_url: String?
|
|
66
|
+
# @owner_base_url: String?
|
|
67
|
+
# @excluded_dirs: Array[String]
|
|
68
|
+
# @template_dir: String
|
|
69
|
+
# @file: Hash[String, untyped]
|
|
70
|
+
# @labels: Hash[String, untyped]
|
|
71
|
+
|
|
72
|
+
# @rbs base_path: String
|
|
73
|
+
# @rbs config_path: String?
|
|
74
|
+
# @rbs organisation: String?
|
|
75
|
+
# @rbs repository: String?
|
|
76
|
+
# @rbs wiki_url: String?
|
|
77
|
+
# @rbs owner_base_url: String?
|
|
78
|
+
# @rbs excluded_dirs: Array[String]?
|
|
79
|
+
# @rbs template_dir: String?
|
|
80
|
+
# @rbs return: void
|
|
81
|
+
def initialize(base_path:, config_path: nil, organisation: nil, repository: nil, wiki_url: nil,
|
|
82
|
+
owner_base_url: nil, excluded_dirs: nil, template_dir: nil)
|
|
83
|
+
@file = load_file(config_path || File.join(base_path, CONFIG_FILENAME))
|
|
84
|
+
@organisation = resolve_organisation(organisation)
|
|
85
|
+
@repository = resolve(repository, 'repository')
|
|
86
|
+
@wiki_url = resolve_wiki_url(wiki_url)
|
|
87
|
+
@owner_base_url = resolve_owner_base_url(owner_base_url)
|
|
88
|
+
@excluded_dirs = excluded_dirs || file.fetch('exclude', DEFAULT_EXCLUDED_DIRS)
|
|
89
|
+
@template_dir = template_dir || file.fetch('template_dir', DEFAULT_TEMPLATE_DIR)
|
|
90
|
+
@labels = deep_merge(DEFAULT_LABELS, file.fetch('labels', EMPTY_HASH))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @rbs return: Array[String]
|
|
94
|
+
def group_bys
|
|
95
|
+
labels.keys
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @rbs group_by: String
|
|
99
|
+
# @rbs return: Array[String]
|
|
100
|
+
def languages(group_by)
|
|
101
|
+
label_for(group_by).fetch('languages', EMPTY_HASH).keys
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @rbs group_by: String
|
|
105
|
+
# @rbs return: Regexp
|
|
106
|
+
def target_regexp(group_by)
|
|
107
|
+
Regexp.new(label_for(group_by).fetch('regexp', ''))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# @rbs group_by: String
|
|
111
|
+
# @rbs language: String
|
|
112
|
+
# @rbs return: String
|
|
113
|
+
def no_declaration(group_by, language)
|
|
114
|
+
language_labels(group_by, language).fetch('no_declaration', '')
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @rbs group_by: String
|
|
118
|
+
# @rbs language: String
|
|
119
|
+
# @rbs return: Array[String]
|
|
120
|
+
def unknown_namespaces(group_by, language)
|
|
121
|
+
language_labels(group_by, language).fetch('unknown_namespaces', [])
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @rbs group_by: String
|
|
125
|
+
# @rbs language: String
|
|
126
|
+
# @rbs return: String
|
|
127
|
+
def llm_target_namespace(group_by, language)
|
|
128
|
+
language_labels(group_by, language).fetch('llm_target_namespace', '')
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @rbs group_by: String
|
|
132
|
+
# @rbs language: String
|
|
133
|
+
# @rbs return: String
|
|
134
|
+
def path_to_template(group_by, language)
|
|
135
|
+
File.join(template_dir, group_by.downcase, "#{language.downcase}.md")
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
attr_reader :file, :labels
|
|
141
|
+
|
|
142
|
+
# Treats empty strings the same as nil so that callers can pass values
|
|
143
|
+
# straight from optional environment variables or CLI flags.
|
|
144
|
+
# @rbs value: String?
|
|
145
|
+
# @rbs return: String?
|
|
146
|
+
def presence(value)
|
|
147
|
+
value unless value.nil? || value.empty?
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @rbs explicit: String?
|
|
151
|
+
# @rbs key: String
|
|
152
|
+
# @rbs return: String?
|
|
153
|
+
def resolve(explicit, key)
|
|
154
|
+
presence(explicit) || presence(file[key])
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# @rbs explicit: String?
|
|
158
|
+
# @rbs return: String?
|
|
159
|
+
def resolve_organisation(explicit)
|
|
160
|
+
resolve(explicit, 'organisation') || presence(ENV.fetch('ORGANISATION_NAME', nil))
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @rbs explicit: String?
|
|
164
|
+
# @rbs return: String?
|
|
165
|
+
def resolve_wiki_url(explicit)
|
|
166
|
+
resolve(explicit, 'wiki_url') || default_wiki_url
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# @rbs explicit: String?
|
|
170
|
+
# @rbs return: String?
|
|
171
|
+
def resolve_owner_base_url(explicit)
|
|
172
|
+
resolve(explicit, 'owner_base_url') || default_owner_base_url
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# @rbs path: String
|
|
176
|
+
# @rbs return: Hash[String, untyped]
|
|
177
|
+
def load_file(path)
|
|
178
|
+
return {} unless File.exist?(path)
|
|
179
|
+
|
|
180
|
+
YAML.safe_load_file(path) || {}
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @rbs group_by: String
|
|
184
|
+
# @rbs return: Hash[String, untyped]
|
|
185
|
+
def label_for(group_by)
|
|
186
|
+
labels.fetch(group_by, EMPTY_HASH)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# @rbs group_by: String
|
|
190
|
+
# @rbs language: String
|
|
191
|
+
# @rbs return: Hash[String, untyped]
|
|
192
|
+
def language_labels(group_by, language)
|
|
193
|
+
label_for(group_by).fetch('languages', EMPTY_HASH).fetch(language, EMPTY_HASH)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# @rbs return: String?
|
|
197
|
+
def default_wiki_url
|
|
198
|
+
"https://github.com/#{organisation}/#{repository}/wiki" if organisation && repository
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# @rbs return: String?
|
|
202
|
+
def default_owner_base_url
|
|
203
|
+
"https://github.com/orgs/#{organisation}/teams/" if organisation
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# @rbs base: Hash[String, untyped]
|
|
207
|
+
# @rbs override: Hash[String, untyped]
|
|
208
|
+
# @rbs return: Hash[String, untyped]
|
|
209
|
+
def deep_merge(base, override)
|
|
210
|
+
base.merge(override) do |_key, old_value, new_value|
|
|
211
|
+
old_value.is_a?(Hash) && new_value.is_a?(Hash) ? deep_merge(old_value, new_value) : new_value
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require_relative 'application'
|
|
6
|
+
|
|
7
|
+
module SpreenWiki
|
|
8
|
+
# Generates the wiki Home page, optionally splitting per-owner content out
|
|
9
|
+
# into `wikis-by-owner/<owner>.md` when home_overflow is enabled. Returns
|
|
10
|
+
# the configured wiki URL (if any) so callers can point at the result.
|
|
11
|
+
class Home < Application
|
|
12
|
+
# @rbs return: String?
|
|
13
|
+
def run
|
|
14
|
+
home_overflow ? write_concise_home_passage : write_home_passage
|
|
15
|
+
config.wiki_url
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# @rbs return: String
|
|
21
|
+
def path_to_wikis_by_owner
|
|
22
|
+
@path_to_wikis_by_owner ||= File.join(base_path, 'wikis-by-owner')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @rbs return: String
|
|
26
|
+
def path_to_home_template
|
|
27
|
+
path = config.path_to_template(group_by, language)
|
|
28
|
+
unless File.exist?(path)
|
|
29
|
+
raise ArgumentError, "Missing Home template: `#{path}`. Ship one there or configure template_dir."
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
path
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @rbs return: Array[String]
|
|
36
|
+
def home_passage
|
|
37
|
+
@home_passage ||= File.readlines(path_to_home_template).append("\n")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @rbs return: void
|
|
41
|
+
def write_home_passage
|
|
42
|
+
FileUtils.rm_rf(path_to_wikis_by_owner)
|
|
43
|
+
owned_wiki_maps.each { |namespace, wikis| append_block(home_passage, namespace, wikis, owned: true) }
|
|
44
|
+
plain_wiki_maps.each { |namespace, wikis| append_block(home_passage, namespace, wikis, owned: false) }
|
|
45
|
+
File.write(path_to_home, home_passage.join.chomp)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @rbs return: void
|
|
49
|
+
def write_concise_home_passage
|
|
50
|
+
FileUtils.mkdir_p(path_to_wikis_by_owner)
|
|
51
|
+
write_per_namespace_files
|
|
52
|
+
write_home_table_of_contents
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @rbs return: void
|
|
56
|
+
def write_per_namespace_files
|
|
57
|
+
owned_wiki_maps.each { |namespace, wikis| write_overflow_block(namespace, wikis, owned: true) }
|
|
58
|
+
plain_wiki_maps.each { |namespace, wikis| write_overflow_block(namespace, wikis, owned: false) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @rbs return: void
|
|
62
|
+
def write_home_table_of_contents
|
|
63
|
+
(owned_wiki_maps.keys + plain_wiki_maps.keys).each { |namespace| home_passage << "- [[#{namespace}]]\n" }
|
|
64
|
+
home_passage << "\n"
|
|
65
|
+
File.write(path_to_home, home_passage.join.chomp)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @rbs passage: Array[String]
|
|
69
|
+
# @rbs namespace: String
|
|
70
|
+
# @rbs wikis: Array[String]
|
|
71
|
+
# @rbs owned: bool
|
|
72
|
+
# @rbs return: void
|
|
73
|
+
def append_block(passage, namespace, wikis, owned:)
|
|
74
|
+
passage << "#{heading_for(namespace, owned:)}\n"
|
|
75
|
+
passage << "\n"
|
|
76
|
+
wikis.each { |wiki| passage << "- [[#{wiki.gsub('.md', '')}]]\n" }
|
|
77
|
+
passage << "\n"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @rbs namespace: String
|
|
81
|
+
# @rbs wikis: Array[String]
|
|
82
|
+
# @rbs owned: bool
|
|
83
|
+
# @rbs return: void
|
|
84
|
+
def write_overflow_block(namespace, wikis, owned:)
|
|
85
|
+
scratch = [] #: Array[String]
|
|
86
|
+
append_block(scratch, namespace, wikis, owned:)
|
|
87
|
+
File.write(File.join(path_to_wikis_by_owner, "#{namespace}.md"), scratch.join.chomp)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @rbs namespace: String
|
|
91
|
+
# @rbs owned: bool
|
|
92
|
+
# @rbs return: String
|
|
93
|
+
def heading_for(namespace, owned:)
|
|
94
|
+
owner_base_url = config.owner_base_url
|
|
95
|
+
return "## #{namespace}" unless owned && owner_base_url
|
|
96
|
+
|
|
97
|
+
"## [#{namespace}](#{owner_base_url + namespace.delete('@')})"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|