docopslab-dev 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.adoc +645 -318
- data/docopslab-dev.gemspec +2 -3
- data/docs/agent/index.md +4 -8
- data/docs/agent/misc/bash-styles.md +470 -0
- data/docs/agent/missions/conduct-release.md +161 -87
- data/docs/agent/missions/setup-new-project.md +228 -134
- data/docs/agent/roles/devops-release-engineer.md +60 -17
- data/docs/agent/roles/docops-engineer.md +84 -20
- data/docs/agent/roles/planner-architect.md +22 -0
- data/docs/agent/roles/product-engineer.md +63 -15
- data/docs/agent/roles/product-manager.md +57 -24
- data/docs/agent/roles/project-manager.md +48 -12
- data/docs/agent/roles/qa-testing-engineer.md +48 -14
- data/docs/agent/roles/tech-docs-manager.md +63 -17
- data/docs/agent/roles/tech-writer.md +68 -14
- data/docs/agent/skills/asciidoc.md +65 -238
- data/docs/agent/skills/bash-cli-dev.md +135 -0
- data/docs/agent/skills/code-commenting.md +143 -106
- data/docs/agent/skills/fix-broken-links.md +145 -100
- data/docs/agent/skills/fix-jekyll-asciidoc-build-errors.md +1 -10
- data/docs/agent/skills/fix-spelling-issues.md +0 -3
- data/docs/agent/skills/git.md +69 -34
- data/docs/agent/skills/github-issues.md +110 -71
- data/docs/agent/skills/rake-cli-dev.md +1 -1
- data/docs/agent/skills/readme-driven-dev.md +1 -0
- data/docs/agent/skills/release-history.md +1 -7
- data/docs/agent/skills/ruby.md +18 -7
- data/docs/agent/skills/schemagraphy-sgyml.md +3 -0
- data/docs/agent/skills/tests-running.md +22 -14
- data/docs/agent/skills/tests-writing.md +51 -28
- data/docs/agent/skills/write-the-docs.md +71 -9
- data/docs/agent/topics/common-project-paths.md +122 -70
- data/docs/agent/topics/dev-tooling-usage.md +70 -77
- data/docs/agent/topics/devops-ci-cd.md +3 -1
- data/docs/agent/topics/product-docs-deployment.md +18 -12
- data/docs/library-readme.adoc +39 -0
- data/lib/docopslab/dev/cast_ops.rb +199 -0
- data/lib/docopslab/dev/config_manager.rb +6 -6
- data/lib/docopslab/dev/data_utils.rb +42 -0
- data/lib/docopslab/dev/file_utils.rb +18 -7
- data/lib/docopslab/dev/git_branch.rb +201 -0
- data/lib/docopslab/dev/git_hooks.rb +17 -11
- data/lib/docopslab/dev/initializer.rb +13 -4
- data/lib/docopslab/dev/library/cache.rb +167 -0
- data/lib/docopslab/dev/library/fetch.rb +209 -0
- data/lib/docopslab/dev/library.rb +328 -0
- data/lib/docopslab/dev/linters.rb +63 -12
- data/lib/docopslab/dev/manifest.rb +28 -0
- data/lib/docopslab/dev/paths.rb +0 -17
- data/lib/docopslab/dev/script_manager.rb +12 -6
- data/lib/docopslab/dev/skim.rb +109 -0
- data/lib/docopslab/dev/spell_check.rb +2 -2
- data/lib/docopslab/dev/sync_ops.rb +94 -33
- data/lib/docopslab/dev/tasks.rb +58 -18
- data/lib/docopslab/dev/version.rb +1 -1
- data/lib/docopslab/dev.rb +75 -35
- data/specs/data/default-manifest.yml +15 -5
- data/specs/data/library-index.yml +22 -0
- data/specs/data/manifest-schema.yaml +142 -4
- data/specs/data/tasks-def.yml +122 -10
- metadata +28 -39
- data/assets/config-packs/actionlint/base.yml +0 -13
- data/assets/config-packs/actionlint/project.yml +0 -13
- data/assets/config-packs/htmlproofer/base.yml +0 -27
- data/assets/config-packs/htmlproofer/project.yml +0 -25
- data/assets/config-packs/rubocop/base.yml +0 -130
- data/assets/config-packs/rubocop/project.yml +0 -8
- data/assets/config-packs/shellcheck/base.shellcheckrc +0 -14
- data/assets/config-packs/subtxt/ai-asciidoc-antipatterns.sub.txt +0 -11
- data/assets/config-packs/vale/asciidoc/ExplicitSectionIDs.yml +0 -8
- data/assets/config-packs/vale/asciidoc/ExtraLineBeforeLevel1.yml +0 -7
- data/assets/config-packs/vale/asciidoc/OneSentencePerLine.yml +0 -8
- data/assets/config-packs/vale/asciidoc/PreferSourceBlocks.yml +0 -8
- data/assets/config-packs/vale/asciidoc/ProperAdmonitions.yml +0 -8
- data/assets/config-packs/vale/asciidoc/ProperDLs.yml +0 -7
- data/assets/config-packs/vale/asciidoc/UncleanListStart.yml +0 -8
- data/assets/config-packs/vale/authoring/ButParagraph.yml +0 -8
- data/assets/config-packs/vale/authoring/ExNotEg.yml +0 -8
- data/assets/config-packs/vale/authoring/LiteralTerms.yml +0 -20
- data/assets/config-packs/vale/authoring/Spelling.yml +0 -679
- data/assets/config-packs/vale/base.ini +0 -38
- data/assets/config-packs/vale/config/scripts/ExplicitSectionIDs.tengo +0 -56
- data/assets/config-packs/vale/config/scripts/ExtraLineBeforeLevel1.tengo +0 -121
- data/assets/config-packs/vale/config/scripts/OneSentencePerLine.tengo +0 -53
- data/assets/config-packs/vale/project.ini +0 -5
- data/assets/hooks/pre-commit +0 -63
- data/assets/hooks/pre-push +0 -72
- data/assets/scripts/adoc_section_ids.rb +0 -50
- data/assets/scripts/build-common.sh +0 -193
- data/assets/scripts/build-docker.sh +0 -64
- data/assets/scripts/build.sh +0 -56
- data/assets/scripts/parse_jekyll_asciidoc_logs.rb +0 -467
- data/assets/templates/Gemfile +0 -7
- data/assets/templates/Rakefile +0 -3
- data/assets/templates/gitignore +0 -69
- data/assets/templates/jekyll-asciidoc-fix.prompt.yml +0 -17
- data/assets/templates/spellcheck.prompt.yml +0 -16
- data/docs/agent/AGENTS.md +0 -229
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'open3'
|
|
4
|
+
require 'pathname'
|
|
5
|
+
require 'sourcerer/util/pathifier'
|
|
4
6
|
|
|
5
7
|
module DocOpsLab
|
|
6
8
|
module Dev
|
|
@@ -9,9 +11,9 @@ module DocOpsLab
|
|
|
9
11
|
def run_rubocop context, file_path=nil, opts_string=''
|
|
10
12
|
context.generate_rubocop_config if context.respond_to?(:generate_rubocop_config)
|
|
11
13
|
|
|
12
|
-
rubocop_config_file =
|
|
14
|
+
rubocop_config_file = Paths::CONFIG_FILES[:rubocop]
|
|
13
15
|
unless File.exist?(rubocop_config_file)
|
|
14
|
-
rubocop_config_file =
|
|
16
|
+
rubocop_config_file = File.join(Paths.config_vendor_dir, 'rubocop.yml') # Fallback to vendor config
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
unless File.exist?(rubocop_config_file)
|
|
@@ -57,9 +59,9 @@ module DocOpsLab
|
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
def run_rubocop_with_filter _context, filter_name
|
|
60
|
-
rubocop_config_file =
|
|
62
|
+
rubocop_config_file = Paths::CONFIG_FILES[:rubocop]
|
|
61
63
|
unless File.exist?(rubocop_config_file)
|
|
62
|
-
rubocop_config_file =
|
|
64
|
+
rubocop_config_file = File.join(Paths.config_vendor_dir, 'rubocop.yml') # Fallback to vendor config
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
unless File.exist?(rubocop_config_file)
|
|
@@ -88,7 +90,16 @@ module DocOpsLab
|
|
|
88
90
|
puts "🐚 Running ShellCheck on #{running_on}"
|
|
89
91
|
|
|
90
92
|
shell_scripts = if scope == :file
|
|
91
|
-
|
|
93
|
+
result = Sourcerer::Util::Pathifier.match(file_path)
|
|
94
|
+
if result.type == :file
|
|
95
|
+
result.enum.to_a
|
|
96
|
+
else
|
|
97
|
+
result.enum.select do |f|
|
|
98
|
+
ext = File.extname(f)
|
|
99
|
+
ext.match?(/\.(sh|bash)$/) ||
|
|
100
|
+
(ext.empty? && FileUtilities.shell_shebang?(f))
|
|
101
|
+
end.sort
|
|
102
|
+
end
|
|
92
103
|
else
|
|
93
104
|
context.find_shell_scripts
|
|
94
105
|
end
|
|
@@ -109,7 +120,14 @@ module DocOpsLab
|
|
|
109
120
|
success = false
|
|
110
121
|
passed = false
|
|
111
122
|
end
|
|
112
|
-
|
|
123
|
+
# Relativize absolute paths so the command works both natively and inside
|
|
124
|
+
# Docker (which mounts $(pwd) as /workspace and sets -w /workspace).
|
|
125
|
+
script_arg = if script.start_with?('/')
|
|
126
|
+
Pathname.new(script).relative_path_from(Pathname.new(Dir.pwd)).to_s
|
|
127
|
+
else
|
|
128
|
+
script
|
|
129
|
+
end
|
|
130
|
+
cmd = "shellcheck --severity=warning #{opts_string} --rcfile=.config/shellcheckrc #{script_arg}".strip
|
|
113
131
|
shellcheck = context.run_with_fallback('shellcheck', cmd)
|
|
114
132
|
unless shellcheck
|
|
115
133
|
success = false
|
|
@@ -183,7 +201,7 @@ module DocOpsLab
|
|
|
183
201
|
puts ' ✅ Vale config up to date' unless context.generate_vale_config(style_override: style_override)
|
|
184
202
|
|
|
185
203
|
# Use the generated config file
|
|
186
|
-
config_file =
|
|
204
|
+
config_file = Paths::CONFIG_FILES[:vale]
|
|
187
205
|
|
|
188
206
|
unless File.exist?(config_file)
|
|
189
207
|
puts "❌ No Vale config found. Run 'labdev:sync:all' to generate one."
|
|
@@ -207,7 +225,33 @@ module DocOpsLab
|
|
|
207
225
|
|
|
208
226
|
# Find AsciiDoc files to check, excluding vendor/ignored directories
|
|
209
227
|
if scope == :file
|
|
210
|
-
|
|
228
|
+
path_result = Sourcerer::Util::Pathifier.match(file_path)
|
|
229
|
+
if path_result.type == :file
|
|
230
|
+
asciidoc_files = [file_path]
|
|
231
|
+
else
|
|
232
|
+
# Directory or glob: enumerate files and apply ext/skip filters from manifest,
|
|
233
|
+
# so that skip patterns (ex: in docopslab-dev.yml) are respected even when a
|
|
234
|
+
# specific directory or glob is passed via the task argument.
|
|
235
|
+
path_config = context.get_path_config('vale')
|
|
236
|
+
skip_paths = path_config[:skip] || []
|
|
237
|
+
exts = path_config[:exts] || []
|
|
238
|
+
pwd = Pathname.pwd
|
|
239
|
+
asciidoc_files = path_result.enum.select do |f|
|
|
240
|
+
normalized = Pathname.new(f).expand_path.relative_path_from(pwd).to_s
|
|
241
|
+
if exts && !exts.empty?
|
|
242
|
+
ext = File.extname(f).delete_prefix('.')
|
|
243
|
+
next false unless exts.include?(ext)
|
|
244
|
+
end
|
|
245
|
+
next false if skip_paths.any? { |p| FileUtilities.file_matches_ignore_pattern?(normalized, p) }
|
|
246
|
+
|
|
247
|
+
true
|
|
248
|
+
end.sort
|
|
249
|
+
if asciidoc_files.empty?
|
|
250
|
+
puts "📄 No AsciiDoc files found to check in #{file_path}"
|
|
251
|
+
return true
|
|
252
|
+
end
|
|
253
|
+
puts "📄 Found #{asciidoc_files.length} AsciiDoc file(s) to check in #{file_path}"
|
|
254
|
+
end
|
|
211
255
|
else
|
|
212
256
|
asciidoc_files = context.find_asciidoc_files
|
|
213
257
|
if asciidoc_files.empty?
|
|
@@ -364,18 +408,25 @@ module DocOpsLab
|
|
|
364
408
|
results.values.all?
|
|
365
409
|
end
|
|
366
410
|
|
|
367
|
-
def run_rubocop_auto_fix
|
|
411
|
+
def run_rubocop_auto_fix context, path: nil
|
|
368
412
|
puts '👮 Running RuboCop auto-correction...'
|
|
369
413
|
|
|
370
|
-
|
|
414
|
+
context.generate_rubocop_config if context.respond_to?(:generate_rubocop_config)
|
|
415
|
+
|
|
416
|
+
rubocop_config_file = Paths::CONFIG_FILES[:rubocop]
|
|
417
|
+
unless File.exist?(rubocop_config_file)
|
|
418
|
+
rubocop_config_file = File.join(Paths.config_vendor_dir, 'rubocop.yml') # Fallback to vendor config
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
unless File.exist?(rubocop_config_file)
|
|
371
422
|
puts "❌ No RuboCop config found. Run 'labdev:init' to create one."
|
|
372
423
|
return false
|
|
373
424
|
end
|
|
374
425
|
|
|
375
|
-
puts "📄 Using config: #{
|
|
426
|
+
puts "📄 Using config: #{rubocop_config_file}"
|
|
376
427
|
|
|
377
428
|
# Build command with optional path
|
|
378
|
-
cmd = "bundle exec rubocop --config #{
|
|
429
|
+
cmd = "bundle exec rubocop --config #{rubocop_config_file} --autocorrect-all"
|
|
379
430
|
if path
|
|
380
431
|
cmd += " #{path}"
|
|
381
432
|
puts "📄 Targeting path: #{path}"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module DocOpsLab
|
|
6
|
+
module Dev
|
|
7
|
+
# Thin wrapper around the project manifest (.config/docopslab-dev.yml).
|
|
8
|
+
# Shared by tools, sync, and library operations that need manifest data.
|
|
9
|
+
module Manifest
|
|
10
|
+
class << self
|
|
11
|
+
# Load a project manifest YAML file.
|
|
12
|
+
# Returns the parsed hash or nil if the file is absent or unreadable.
|
|
13
|
+
def load path=Dev::MANIFEST_PATH
|
|
14
|
+
return nil unless File.exist?(path)
|
|
15
|
+
|
|
16
|
+
YAML.load_file(path)
|
|
17
|
+
rescue StandardError
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# True if data is a non-empty Hash.
|
|
22
|
+
def valid? data
|
|
23
|
+
data.is_a?(Hash) && !data.empty?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/docopslab/dev/paths.rb
CHANGED
|
@@ -9,23 +9,6 @@ module DocOpsLab
|
|
|
9
9
|
File.expand_path('../../..', __dir__)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
# Asset directories in gem
|
|
13
|
-
def self.gem_assets
|
|
14
|
-
File.join(gem_root, 'assets')
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def self.gem_config_packs
|
|
18
|
-
File.join(gem_assets, 'config-packs')
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def self.gem_hooks
|
|
22
|
-
File.join(gem_assets, 'hooks')
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def self.gem_scripts
|
|
26
|
-
File.join(gem_assets, 'scripts')
|
|
27
|
-
end
|
|
28
|
-
|
|
29
12
|
# Config vendor directory (where config packs are synced to)
|
|
30
13
|
def self.config_vendor_dir
|
|
31
14
|
'.config/.vendor/docopslab'
|
|
@@ -9,8 +9,13 @@ module DocOpsLab
|
|
|
9
9
|
def sync_scripts
|
|
10
10
|
puts '📜 Syncing common scripts from DocOps Lab...'
|
|
11
11
|
|
|
12
|
-
unless
|
|
13
|
-
puts '❌
|
|
12
|
+
unless Library.available?
|
|
13
|
+
puts '❌ Library not available; run `labdev:sync:library` to fetch.'
|
|
14
|
+
return false
|
|
15
|
+
end
|
|
16
|
+
scripts_source = Library.resolve('scripts')
|
|
17
|
+
unless scripts_source && Dir.exist?(scripts_source)
|
|
18
|
+
puts '❌ scripts not found in library; run `labdev:sync:library` to fetch.'
|
|
14
19
|
return false
|
|
15
20
|
end
|
|
16
21
|
|
|
@@ -20,7 +25,7 @@ module DocOpsLab
|
|
|
20
25
|
|
|
21
26
|
synced_count = 0
|
|
22
27
|
|
|
23
|
-
Dir.glob("#{
|
|
28
|
+
Dir.glob("#{scripts_source}/*").each do |script_path|
|
|
24
29
|
next unless File.file?(script_path)
|
|
25
30
|
|
|
26
31
|
script_name = File.basename(script_path)
|
|
@@ -47,14 +52,15 @@ module DocOpsLab
|
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
def list_script_templates
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
scripts_source = Library.resolve('scripts')
|
|
56
|
+
unless scripts_source && Dir.exist?(scripts_source)
|
|
57
|
+
puts '❌ scripts not found in library; run `labdev:sync:library` to fetch.'
|
|
52
58
|
return false
|
|
53
59
|
end
|
|
54
60
|
|
|
55
61
|
puts '📜 Available script templates:'
|
|
56
62
|
|
|
57
|
-
Dir.glob("#{
|
|
63
|
+
Dir.glob("#{scripts_source}/*").each do |script_path|
|
|
58
64
|
next unless File.file?(script_path)
|
|
59
65
|
|
|
60
66
|
script_name = File.basename(script_path)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'asciisourcerer'
|
|
5
|
+
require 'sourcerer/util/pathifier'
|
|
6
|
+
|
|
7
|
+
module DocOpsLab
|
|
8
|
+
module Dev
|
|
9
|
+
# Source skimming via Sourcerer::SourceSkim
|
|
10
|
+
module Skim
|
|
11
|
+
ADOC_EXTS = %w[.adoc .asc .ad .asciidoc].freeze
|
|
12
|
+
MD_EXTS = %w[.md .markdown].freeze
|
|
13
|
+
ALL_EXTS = (ADOC_EXTS + MD_EXTS).freeze
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# Skim all supported file types (AsciiDoc + Markdown), format auto-detected.
|
|
17
|
+
def run path, form: nil, syntax: nil
|
|
18
|
+
run_with_format(path, exts: ALL_EXTS, form: form, syntax: syntax)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Skim AsciiDoc source files only.
|
|
22
|
+
def run_adoc path, form: nil, syntax: nil
|
|
23
|
+
run_with_format(path, exts: ADOC_EXTS, form: form, syntax: syntax, default_forms: [:tree])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Skim Markdown source files only, with optional upstream:local overlay support.
|
|
27
|
+
def run_md path, form: nil, syntax: nil
|
|
28
|
+
run_with_format(path, exts: MD_EXTS, form: form, syntax: syntax, overlay: true)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def run_with_format path, exts:, form: nil, syntax: nil, default_forms: nil, overlay: false
|
|
34
|
+
unless path
|
|
35
|
+
puts '❌ Path is required.'
|
|
36
|
+
puts 'Usage: bundle exec rake labdev:skim[path,form,syntax]'
|
|
37
|
+
return
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
forms = form ? parse_forms(form) : default_forms
|
|
41
|
+
file_paths = overlay ? resolve_overlay_paths(path, exts) : resolve_paths(path, exts)
|
|
42
|
+
|
|
43
|
+
if file_paths.empty?
|
|
44
|
+
ext_desc = exts.size == 1 ? exts.first : exts.join(', ')
|
|
45
|
+
warn "No #{ext_desc} files found for: #{path}"
|
|
46
|
+
return
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
results = {}
|
|
50
|
+
cats = Sourcerer::SourceSkim::DEFAULT_CATEGORIES - [:attributes_custom]
|
|
51
|
+
file_paths.each do |fp|
|
|
52
|
+
skim_opts = { categories: cats }
|
|
53
|
+
skim_opts[:forms] = forms if forms
|
|
54
|
+
results[fp] = Sourcerer::SourceSkim.skim_file(fp, **skim_opts)
|
|
55
|
+
end
|
|
56
|
+
portable = JSON.parse(JSON.generate(results))
|
|
57
|
+
|
|
58
|
+
output_syntax = resolve_syntax(syntax, form)
|
|
59
|
+
puts output_syntax == :json ? JSON.pretty_generate(portable) : portable.to_yaml
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Resolve file paths with optional upstream:local overlay.
|
|
63
|
+
#
|
|
64
|
+
# When path_arg contains ':', split into upstream_dir:local_dir. Local files shadow
|
|
65
|
+
# upstream files that share the same relative path; local-only files are appended.
|
|
66
|
+
def resolve_overlay_paths path_arg, exts
|
|
67
|
+
parts = path_arg.split(':', 2).map(&:strip)
|
|
68
|
+
return resolve_paths(parts[0], exts) if parts.size == 1
|
|
69
|
+
|
|
70
|
+
upstream_dir, local_dir = parts
|
|
71
|
+
upstream_map = build_relative_map(upstream_dir, exts)
|
|
72
|
+
local_map = build_relative_map(local_dir, exts)
|
|
73
|
+
upstream_map.merge(local_map).values
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def build_relative_map dir_path, exts
|
|
77
|
+
dir_path = dir_path.chomp('/')
|
|
78
|
+
return {} unless File.directory?(dir_path)
|
|
79
|
+
|
|
80
|
+
abs_base = File.expand_path(dir_path)
|
|
81
|
+
Sourcerer::Util::Pathifier.match(dir_path).enum
|
|
82
|
+
.select { |p| exts.any? { |e| p.end_with?(e) } }
|
|
83
|
+
.each_with_object({}) do |abs_path, map|
|
|
84
|
+
map[abs_path.sub("#{abs_base}/", '')] = abs_path
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def parse_forms form
|
|
89
|
+
form.split(',').map { |f| f.strip.to_sym }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def resolve_paths path, exts
|
|
93
|
+
result = Sourcerer::Util::Pathifier.match(path)
|
|
94
|
+
result.type == :dir ? result.enum.select { |p| exts.any? { |e| p.end_with?(e) } } : result.enum.to_a
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def resolve_syntax syntax, form
|
|
98
|
+
return :yaml unless form
|
|
99
|
+
|
|
100
|
+
syntax = 'yaml' if syntax == 'yml'
|
|
101
|
+
puts
|
|
102
|
+
return syntax.to_sym if syntax
|
|
103
|
+
|
|
104
|
+
:json
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -146,8 +146,8 @@ module DocOpsLab
|
|
|
146
146
|
require 'erb'
|
|
147
147
|
|
|
148
148
|
unless agent_prompt
|
|
149
|
-
template_path =
|
|
150
|
-
agent_prompt = File.read(template_path) if File.exist?(template_path)
|
|
149
|
+
template_path = Library.resolve('templates/spellcheck.prompt.yml')
|
|
150
|
+
agent_prompt = File.read(template_path) if template_path && File.exist?(template_path)
|
|
151
151
|
end
|
|
152
152
|
|
|
153
153
|
# Create the YAML content with ERB templating for better formatting
|
|
@@ -6,36 +6,35 @@ require 'pathname'
|
|
|
6
6
|
|
|
7
7
|
module DocOpsLab
|
|
8
8
|
module Dev
|
|
9
|
+
# rubocop:disable Metrics/ModuleLength
|
|
9
10
|
module SyncOps
|
|
10
|
-
# rubocop
|
|
11
|
+
# rubocop:disable Metrics/ClassLength
|
|
11
12
|
class << self
|
|
12
13
|
def install_vale_styles context
|
|
13
|
-
return unless File.exist?(
|
|
14
|
+
return unless File.exist?(Paths::CONFIG_FILES[:vale]) && context.tool_available?('vale')
|
|
14
15
|
|
|
15
|
-
puts "📚 Syncing Vale styles using Packages key in #{
|
|
16
|
-
context.run_with_fallback('vale', "vale --config=#{
|
|
16
|
+
puts "📚 Syncing Vale styles using Packages key in #{Paths::CONFIG_FILES[:vale]} (local and remote packages)"
|
|
17
|
+
context.run_with_fallback('vale', "vale --config=#{Paths::CONFIG_FILES[:vale]} sync")
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def sync_vale_styles context, local: false
|
|
20
21
|
puts '📚 Syncing Vale styles...'
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
unless Library.available?
|
|
24
|
+
puts '❌ Library not available; run `labdev:sync:library` to fetch.'
|
|
25
|
+
return false
|
|
26
|
+
end
|
|
27
|
+
styles_source_root = Library.resolve('config-packs/vale')
|
|
28
|
+
unless styles_source_root
|
|
29
|
+
puts '❌ config-packs/vale not found in library; run `labdev:sync:library` to fetch.'
|
|
30
|
+
return false
|
|
31
|
+
end
|
|
29
32
|
styles_dest_root = '.config/.vendor/vale/styles'
|
|
30
33
|
FileUtils.mkdir_p(styles_dest_root)
|
|
31
34
|
|
|
32
35
|
# Get the list of local styles from tools.yml
|
|
33
36
|
begin
|
|
34
|
-
tools_yml_path =
|
|
35
|
-
'gems/docopslab-dev/specs/data/tools.yml'
|
|
36
|
-
else
|
|
37
|
-
File.join(GEM_ROOT, 'specs', 'data', 'tools.yml')
|
|
38
|
-
end
|
|
37
|
+
tools_yml_path = Dev.tools_def_path
|
|
39
38
|
|
|
40
39
|
style_paths_array = YAML.load_file(tools_yml_path)
|
|
41
40
|
.find { |t| t['slug'] == 'vale' }['packaging']['packages']
|
|
@@ -77,7 +76,7 @@ module DocOpsLab
|
|
|
77
76
|
# If not local-only, also run Vale sync for remote styles
|
|
78
77
|
unless local
|
|
79
78
|
puts '📦 Syncing remote Vale packages...'
|
|
80
|
-
vale_config =
|
|
79
|
+
vale_config = Paths::CONFIG_FILES[:vale]
|
|
81
80
|
context.generate_vale_config unless File.exist?(vale_config)
|
|
82
81
|
context.run_with_fallback('vale', "vale --config=#{vale_config} sync")
|
|
83
82
|
end
|
|
@@ -92,6 +91,12 @@ module DocOpsLab
|
|
|
92
91
|
docs_entries = manifest['docs']
|
|
93
92
|
return false unless docs_entries.is_a?(Array)
|
|
94
93
|
|
|
94
|
+
lib_root = Library.root
|
|
95
|
+
unless lib_root
|
|
96
|
+
puts '❌ Library not available; run `labdev:sync:library` to fetch.'
|
|
97
|
+
return false
|
|
98
|
+
end
|
|
99
|
+
|
|
95
100
|
puts '📚 Syncing documentation files...'
|
|
96
101
|
|
|
97
102
|
synced_count = 0
|
|
@@ -107,16 +112,14 @@ module DocOpsLab
|
|
|
107
112
|
next unless source_pattern
|
|
108
113
|
next if synced # Only collect exclusions
|
|
109
114
|
|
|
110
|
-
# Resolve source file path
|
|
115
|
+
# Resolve source file path relative to library root
|
|
111
116
|
if source_pattern.include?('*')
|
|
112
|
-
|
|
113
|
-
source_glob = File.join(GEM_ROOT, source_pattern)
|
|
117
|
+
source_glob = File.join(lib_root, source_pattern)
|
|
114
118
|
Dir.glob(source_glob).each do |source_file|
|
|
115
119
|
excluded_files.add(source_file) if File.file?(source_file)
|
|
116
120
|
end
|
|
117
121
|
else
|
|
118
|
-
|
|
119
|
-
source_file = File.join(GEM_ROOT, source_pattern)
|
|
122
|
+
source_file = File.join(lib_root, source_pattern)
|
|
120
123
|
excluded_files.add(source_file) if File.exist?(source_file)
|
|
121
124
|
end
|
|
122
125
|
end
|
|
@@ -134,8 +137,7 @@ module DocOpsLab
|
|
|
134
137
|
|
|
135
138
|
# Check if source is a glob pattern
|
|
136
139
|
if source_pattern.include?('*')
|
|
137
|
-
|
|
138
|
-
source_glob = File.join(GEM_ROOT, source_pattern)
|
|
140
|
+
source_glob = File.join(lib_root, source_pattern)
|
|
139
141
|
matching_files = Dir.glob(source_glob)
|
|
140
142
|
|
|
141
143
|
if matching_files.empty?
|
|
@@ -147,13 +149,18 @@ module DocOpsLab
|
|
|
147
149
|
next unless File.file?(source_file)
|
|
148
150
|
next if sources_checked.include?(source_file)
|
|
149
151
|
|
|
150
|
-
# Skip if explicitly excluded
|
|
151
152
|
if excluded_files.include?(source_file)
|
|
152
153
|
puts " ⏭️ Skipped #{File.basename(source_file)} (explicitly excluded)"
|
|
153
154
|
next
|
|
154
155
|
end
|
|
155
156
|
|
|
156
|
-
#
|
|
157
|
+
# Guard: docs/agent/AGENTS.md was relocated to templates/AGENTS.markdown.
|
|
158
|
+
if source_file.end_with?('docs/agent/AGENTS.md')
|
|
159
|
+
puts ' ⏭️ Skipped docs/agent/AGENTS.md \
|
|
160
|
+
(relocated to templates/AGENTS.markdown; remove from manifest)'
|
|
161
|
+
next
|
|
162
|
+
end
|
|
163
|
+
|
|
157
164
|
filename = File.basename(source_file)
|
|
158
165
|
target_file = File.join(target_path, filename)
|
|
159
166
|
|
|
@@ -163,11 +170,11 @@ module DocOpsLab
|
|
|
163
170
|
skipped_count += 1 if result == :skipped
|
|
164
171
|
end
|
|
165
172
|
else # Single file
|
|
166
|
-
source_file = File.join(
|
|
173
|
+
source_file = File.join(lib_root, source_pattern)
|
|
167
174
|
|
|
168
175
|
unless File.exist?(source_file)
|
|
169
176
|
puts " ❌ Source file not found: #{source_file}"
|
|
170
|
-
puts " Run 'bundle exec rake
|
|
177
|
+
puts " Run 'bundle exec rake labdev:sync:library` then 'labdev:sync:docs' to refresh"
|
|
171
178
|
next
|
|
172
179
|
end
|
|
173
180
|
|
|
@@ -179,6 +186,12 @@ module DocOpsLab
|
|
|
179
186
|
next
|
|
180
187
|
end
|
|
181
188
|
|
|
189
|
+
# Guard: docs/agent/AGENTS.md was relocated to templates/AGENTS.markdown.
|
|
190
|
+
if source_file.end_with?('docs/agent/AGENTS.md')
|
|
191
|
+
puts ' ⏭️ Skipped docs/agent/AGENTS.md (relocated to templates/AGENTS.markdown; remove from manifest)'
|
|
192
|
+
next
|
|
193
|
+
end
|
|
194
|
+
|
|
182
195
|
sources_checked << source_file
|
|
183
196
|
|
|
184
197
|
result = copy_doc_file(source_file, target_path, synced: synced, force: force)
|
|
@@ -198,6 +211,52 @@ module DocOpsLab
|
|
|
198
211
|
ScriptManager.sync_scripts
|
|
199
212
|
end
|
|
200
213
|
|
|
214
|
+
def sync_templates context, force: false
|
|
215
|
+
manifest = context.load_manifest
|
|
216
|
+
return false unless manifest
|
|
217
|
+
|
|
218
|
+
template_entries = manifest['templates']
|
|
219
|
+
return false unless template_entries.is_a?(Array)
|
|
220
|
+
|
|
221
|
+
lib_root = Library.root
|
|
222
|
+
unless lib_root
|
|
223
|
+
puts '❌ Library not available; run `labdev:sync:library` to fetch.'
|
|
224
|
+
return false
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
puts '📄 Syncing template files...'
|
|
228
|
+
|
|
229
|
+
synced_count = 0
|
|
230
|
+
skipped_count = 0
|
|
231
|
+
|
|
232
|
+
template_entries.each do |entry|
|
|
233
|
+
source_rel = entry['source']
|
|
234
|
+
target_path = entry['target']
|
|
235
|
+
synced = entry.fetch('synced', false)
|
|
236
|
+
|
|
237
|
+
next unless source_rel && target_path
|
|
238
|
+
|
|
239
|
+
source_file = File.join(lib_root, source_rel)
|
|
240
|
+
|
|
241
|
+
unless File.exist?(source_file)
|
|
242
|
+
puts " \u274c Template source not found: #{source_rel}"
|
|
243
|
+
puts ' Run `bundle exec rake labdev:sync:library` to fetch the latest library.'
|
|
244
|
+
next
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
result = copy_doc_file(source_file, target_path, synced: synced, force: force)
|
|
248
|
+
synced_count += 1 if result == :copied
|
|
249
|
+
skipped_count += 1 if result == :skipped
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
puts "\u2705 Synced #{synced_count} template file(s)" if synced_count.positive?
|
|
253
|
+
if skipped_count.positive?
|
|
254
|
+
puts "\u2139\ufe0f Skipped #{skipped_count} existing template(s) (use --force to overwrite)"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
synced_count.positive? || skipped_count.positive?
|
|
258
|
+
end
|
|
259
|
+
|
|
201
260
|
def sync_config_files context, tool_filter: :all, offline: false
|
|
202
261
|
# Validate tool filter parameter
|
|
203
262
|
unless tool_filter == :all || tool_filter.is_a?(String) || tool_filter.is_a?(Symbol)
|
|
@@ -222,8 +281,9 @@ module DocOpsLab
|
|
|
222
281
|
return false
|
|
223
282
|
end
|
|
224
283
|
|
|
225
|
-
|
|
226
|
-
|
|
284
|
+
config_packs_root = Library.resolve('config-packs')
|
|
285
|
+
unless config_packs_root && Dir.exist?(config_packs_root)
|
|
286
|
+
puts '❌ config-packs not found in library; run `labdev:sync:library` to fetch.'
|
|
227
287
|
return false
|
|
228
288
|
end
|
|
229
289
|
|
|
@@ -270,7 +330,7 @@ module DocOpsLab
|
|
|
270
330
|
next
|
|
271
331
|
end
|
|
272
332
|
|
|
273
|
-
source_path = File.join(
|
|
333
|
+
source_path = File.join(config_packs_root, source_rel)
|
|
274
334
|
|
|
275
335
|
unless File.exist?(source_path)
|
|
276
336
|
puts " ❌ Source not found: #{source_rel}"
|
|
@@ -375,7 +435,7 @@ module DocOpsLab
|
|
|
375
435
|
obsolete_files = []
|
|
376
436
|
# Common vendor paths to check for obsolete files
|
|
377
437
|
vendor_patterns = [
|
|
378
|
-
File.join(
|
|
438
|
+
File.join(Paths.config_vendor_dir, '**', '*')
|
|
379
439
|
]
|
|
380
440
|
vendor_patterns.each do |pattern|
|
|
381
441
|
Dir.glob(pattern).each do |file_path|
|
|
@@ -462,7 +522,8 @@ module DocOpsLab
|
|
|
462
522
|
end
|
|
463
523
|
end
|
|
464
524
|
end
|
|
465
|
-
# rubocop
|
|
525
|
+
# rubocop:enable Metrics/ClassLength
|
|
466
526
|
end
|
|
527
|
+
# rubocop:enable Metrics/ModuleLength
|
|
467
528
|
end
|
|
468
529
|
end
|