releasehx 0.1.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 +7 -0
- data/README.adoc +2915 -0
- data/bin/releasehx +7 -0
- data/bin/rhx +7 -0
- data/bin/rhx-mcp +7 -0
- data/bin/sourcerer +32 -0
- data/build/docs/CNAME +1 -0
- data/build/docs/Gemfile.lock +95 -0
- data/build/docs/_config.yml +36 -0
- data/build/docs/config-reference.adoc +4104 -0
- data/build/docs/config-reference.json +1546 -0
- data/build/docs/index.adoc +2915 -0
- data/build/docs/landing.adoc +21 -0
- data/build/docs/manpage.adoc +68 -0
- data/build/docs/releasehx.1 +281 -0
- data/build/docs/releasehx_readme.html +367 -0
- data/build/docs/sample-config.adoc +9 -0
- data/build/docs/sample-config.yml +251 -0
- data/build/docs/schemagraphy_readme.html +0 -0
- data/build/docs/sourcerer_readme.html +46 -0
- data/build/snippets/helpscreen.txt +29 -0
- data/lib/docopslab/mcp/asset_packager.rb +30 -0
- data/lib/docopslab/mcp/manifest.rb +67 -0
- data/lib/docopslab/mcp/resource_pack.rb +46 -0
- data/lib/docopslab/mcp/server.rb +92 -0
- data/lib/docopslab/mcp.rb +6 -0
- data/lib/releasehx/cli.rb +937 -0
- data/lib/releasehx/configuration.rb +215 -0
- data/lib/releasehx/generated.rb +17 -0
- data/lib/releasehx/helpers.rb +58 -0
- data/lib/releasehx/mcp/asset_packager.rb +21 -0
- data/lib/releasehx/mcp/assets/agent-config-guide.md +178 -0
- data/lib/releasehx/mcp/assets/config-def.yml +1426 -0
- data/lib/releasehx/mcp/assets/config-reference.adoc +4104 -0
- data/lib/releasehx/mcp/assets/config-reference.json +1546 -0
- data/lib/releasehx/mcp/assets/sample-config.yml +251 -0
- data/lib/releasehx/mcp/manifest.rb +18 -0
- data/lib/releasehx/mcp/resource_pack.rb +26 -0
- data/lib/releasehx/mcp/server.rb +57 -0
- data/lib/releasehx/mcp.rb +7 -0
- data/lib/releasehx/ops/check_ops.rb +136 -0
- data/lib/releasehx/ops/draft_ops.rb +173 -0
- data/lib/releasehx/ops/enrich_ops.rb +221 -0
- data/lib/releasehx/ops/template_ops.rb +61 -0
- data/lib/releasehx/ops/write_ops.rb +124 -0
- data/lib/releasehx/rest/clients/github.yml +46 -0
- data/lib/releasehx/rest/clients/gitlab.yml +31 -0
- data/lib/releasehx/rest/clients/jira.yml +31 -0
- data/lib/releasehx/rest/yaml_client.rb +418 -0
- data/lib/releasehx/rhyml/adapter.rb +740 -0
- data/lib/releasehx/rhyml/change.rb +167 -0
- data/lib/releasehx/rhyml/liquid.rb +13 -0
- data/lib/releasehx/rhyml/loaders.rb +37 -0
- data/lib/releasehx/rhyml/mappings/github.yaml +60 -0
- data/lib/releasehx/rhyml/mappings/gitlab.yaml +73 -0
- data/lib/releasehx/rhyml/mappings/jira.yaml +29 -0
- data/lib/releasehx/rhyml/mappings/verb_past_tenses.yml +98 -0
- data/lib/releasehx/rhyml/release.rb +144 -0
- data/lib/releasehx/rhyml.rb +15 -0
- data/lib/releasehx/sgyml/helpers.rb +45 -0
- data/lib/releasehx/transforms/adf_to_markdown.rb +307 -0
- data/lib/releasehx/version.rb +7 -0
- data/lib/releasehx.rb +69 -0
- data/lib/schemagraphy/attribute_resolver.rb +48 -0
- data/lib/schemagraphy/cfgyml/definition.rb +90 -0
- data/lib/schemagraphy/cfgyml/doc_builder.rb +52 -0
- data/lib/schemagraphy/cfgyml/path_reference.rb +24 -0
- data/lib/schemagraphy/data_query/json_pointer.rb +42 -0
- data/lib/schemagraphy/loader.rb +59 -0
- data/lib/schemagraphy/regexp_utils.rb +215 -0
- data/lib/schemagraphy/safe_expression.rb +189 -0
- data/lib/schemagraphy/schema_utils.rb +124 -0
- data/lib/schemagraphy/tag_utils.rb +32 -0
- data/lib/schemagraphy/templating.rb +104 -0
- data/lib/schemagraphy.rb +17 -0
- data/lib/sourcerer/builder.rb +120 -0
- data/lib/sourcerer/jekyll/bootstrapper.rb +78 -0
- data/lib/sourcerer/jekyll/liquid/file_system.rb +74 -0
- data/lib/sourcerer/jekyll/liquid/filters.rb +215 -0
- data/lib/sourcerer/jekyll/liquid/tags.rb +44 -0
- data/lib/sourcerer/jekyll/monkeypatches.rb +73 -0
- data/lib/sourcerer/jekyll.rb +26 -0
- data/lib/sourcerer/plaintext_converter.rb +75 -0
- data/lib/sourcerer/templating.rb +190 -0
- data/lib/sourcerer.rb +322 -0
- data/specs/data/api-client-schema.yaml +160 -0
- data/specs/data/config-def.yml +1426 -0
- data/specs/data/mcp-manifest.yml +50 -0
- data/specs/data/rhyml-mapping-schema.yaml +410 -0
- data/specs/data/rhyml-schema.yaml +152 -0
- metadata +376 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'psych'
|
|
4
|
+
require_relative '../schemagraphy/loader'
|
|
5
|
+
|
|
6
|
+
module ReleaseHx
|
|
7
|
+
# Manages the application's configuration by loading and merging settings
|
|
8
|
+
# from a definition file and a user-provided configuration file.
|
|
9
|
+
class Configuration
|
|
10
|
+
# Public: Global gem attributes copied from the generated README attributes.
|
|
11
|
+
#
|
|
12
|
+
# This constant is used to locate the internal schema/definition file and
|
|
13
|
+
# to determine sane defaults for the user's configuration path.
|
|
14
|
+
# @return [Hash] Global attributes for the gem.
|
|
15
|
+
GEM_GLOBALS = ReleaseHx::ATTRIBUTES[:globals].freeze
|
|
16
|
+
GEM_ROOT_PATH = File.expand_path('../..', __dir__).freeze
|
|
17
|
+
# @return [String] The path to the internal configuration definition file.
|
|
18
|
+
CONFIG_DEF_PATH = File.join(
|
|
19
|
+
GEM_ROOT_PATH,
|
|
20
|
+
GEM_GLOBALS['gem_config_definition_path']).freeze
|
|
21
|
+
# @return [String] The default path to the user's configuration file.
|
|
22
|
+
DEFAULT_CONFIG_PATH = File.join(
|
|
23
|
+
File.expand_path('.'),
|
|
24
|
+
GEM_GLOBALS['app_default_config_path'] || 'releasehx.yml').freeze
|
|
25
|
+
|
|
26
|
+
# @param settings [Hash] A hash of configuration settings.
|
|
27
|
+
def initialize settings = {}
|
|
28
|
+
@settings = settings
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Loads and merges the user configuration with the internal definition/schema
|
|
32
|
+
# and performs the SGYML precompilation and staged rendering.
|
|
33
|
+
#
|
|
34
|
+
# This method returns a fully-resolved Configuration object suitable for use across the application.
|
|
35
|
+
#
|
|
36
|
+
# @param user_config_path [String] Path to the user's config file (defaults to app default).
|
|
37
|
+
# @param definition_path [String] Path to the internal schema/definition file.
|
|
38
|
+
# @return [Configuration] The final, merged configuration object.
|
|
39
|
+
def self.load user_config_path = DEFAULT_CONFIG_PATH, definition_path = CONFIG_DEF_PATH
|
|
40
|
+
ReleaseHx.logger.debug "Loading configuration from: #{user_config_path}"
|
|
41
|
+
|
|
42
|
+
# Use SchemaGraphy to load config definition with resolved attributes
|
|
43
|
+
attrs = ReleaseHx::ATTRIBUTES[:globals]
|
|
44
|
+
definition = SchemaGraphy::Loader.load_yaml_with_attributes(definition_path, attrs)
|
|
45
|
+
SchemaGraphy::SchemaUtils.crawl_meta(definition, 'history.head')
|
|
46
|
+
|
|
47
|
+
user_config = File.exist?(user_config_path) ? SchemaGraphy::Loader.load_yaml_with_tags(user_config_path) : {}
|
|
48
|
+
merged_settings = apply_schema(definition['properties'], user_config)
|
|
49
|
+
config = new(merged_settings)
|
|
50
|
+
|
|
51
|
+
ReleaseHx::SgymlHelpers.precompile_from_schema!(
|
|
52
|
+
config.settings,
|
|
53
|
+
definition, # should contain $schema or be the schema
|
|
54
|
+
scope: { 'config' => config.settings })
|
|
55
|
+
ReleaseHx::SgymlHelpers.render_stage_fields!(config.settings, :load)
|
|
56
|
+
|
|
57
|
+
config
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Provides convenient dot-style access to top-level configuration sections.
|
|
61
|
+
#
|
|
62
|
+
# Example:
|
|
63
|
+
# config = ReleaseHx::Configuration.load
|
|
64
|
+
# source = config.origin['source']
|
|
65
|
+
#
|
|
66
|
+
# This method returns the sub-hash stored under the given key name when present;
|
|
67
|
+
# otherwise it delegates to the normal method_missing implementation.
|
|
68
|
+
def method_missing(name, *args, &)
|
|
69
|
+
# If settings has a key matching the method name, return that sub-hash
|
|
70
|
+
key_str = name.to_s
|
|
71
|
+
return settings[key_str] if settings.key?(key_str)
|
|
72
|
+
|
|
73
|
+
super
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Indicates whether the configuration has a setting for the given key.
|
|
77
|
+
#
|
|
78
|
+
# @param name [Symbol] The method name being queried.
|
|
79
|
+
# @param include_private [Boolean] Whether to include private methods.
|
|
80
|
+
# @return [Boolean] True if the key exists in settings; otherwise false.
|
|
81
|
+
def respond_to_missing? name, include_private = false
|
|
82
|
+
settings.key?(name.to_s) || super
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @return [Hash] The underlying hash of configuration settings.
|
|
86
|
+
attr_reader :settings
|
|
87
|
+
|
|
88
|
+
# Provides bracket access to configuration settings.
|
|
89
|
+
#
|
|
90
|
+
# Accepts String or Symbol keys and returns the stored value (or nil).
|
|
91
|
+
#
|
|
92
|
+
# @param key [String, Symbol] The key to access.
|
|
93
|
+
# @return [Object] The value associated with the key.
|
|
94
|
+
def [] key
|
|
95
|
+
@settings[key.to_s]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @api private
|
|
99
|
+
# Recursively applies schema defaults and normalizes the user's config.
|
|
100
|
+
#
|
|
101
|
+
# Special handling:
|
|
102
|
+
# - A user value of the literal string "$nil" will explicitly remove the
|
|
103
|
+
# property (treated as nil)
|
|
104
|
+
# - Unknown user-supplied keys are preserved so extensions are not lost
|
|
105
|
+
#
|
|
106
|
+
# @param schema_properties [Hash] The properties from the schema.
|
|
107
|
+
# @param user_hash [Hash] The user's configuration hash.
|
|
108
|
+
# @return [Hash] The merged hash with defaults applied.
|
|
109
|
+
def self.apply_schema schema_properties, user_hash
|
|
110
|
+
final_hash = {}
|
|
111
|
+
|
|
112
|
+
(schema_properties || {}).each do |prop_key, prop_def|
|
|
113
|
+
user_val = user_hash.fetch(prop_key, nil)
|
|
114
|
+
# Skip processing this property if user explicitly set it to $nil
|
|
115
|
+
unless user_val.to_s.strip == '$nil'
|
|
116
|
+
final_val = apply_property(prop_def, user_val)
|
|
117
|
+
final_hash[prop_key] = final_val
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Preserves extra user-supplied keys that aren't in the definition/schema
|
|
122
|
+
user_hash.each do |unk_key, unk_val|
|
|
123
|
+
final_hash[unk_key] = unk_val unless final_hash.key?(unk_key) || unk_val.to_s.strip == '$nil'
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
final_hash
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @api private
|
|
130
|
+
# Applies a single property definition from the schema to a user-supplied value.
|
|
131
|
+
#
|
|
132
|
+
# Handles nested objects and array defaults declared in the schema.
|
|
133
|
+
# User values are preserved unless they are absent (nil) or explicitly set to the literal "$nil" marker.
|
|
134
|
+
#
|
|
135
|
+
# @param prop_def [Hash] The property definition from the schema.
|
|
136
|
+
# @param user_val [Object] The user's value for this property.
|
|
137
|
+
# @return [Object] The final value for the property.
|
|
138
|
+
def self.apply_property prop_def, user_val
|
|
139
|
+
return nil if user_val.to_s.strip == '$nil'
|
|
140
|
+
|
|
141
|
+
default_val = prop_def['dflt'] if prop_def.key?('dflt')
|
|
142
|
+
val = user_val.nil? ? default_val : user_val
|
|
143
|
+
|
|
144
|
+
# If this prop has nested "properties", treat as a sub-object
|
|
145
|
+
if prop_def['properties']
|
|
146
|
+
val_hash = val.is_a?(Hash) ? val : {}
|
|
147
|
+
# recursively handle sub-properties
|
|
148
|
+
val = apply_schema(prop_def['properties'], val_hash)
|
|
149
|
+
elsif prop_def['type'] == 'ArrayList' && val.nil?
|
|
150
|
+
# It's an array type with a default and we have no user value
|
|
151
|
+
val = default_val || []
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
val
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# List of configuration validation rules used by {validate_config}.
|
|
158
|
+
# Each rule is a Hash with :scopes, :message and :check (callable).
|
|
159
|
+
#
|
|
160
|
+
# The :check callable should raise an ArgumentError when validation fails.
|
|
161
|
+
#
|
|
162
|
+
# @return [Array<Hash>] A list of validation rules.
|
|
163
|
+
VALIDATION_RULES = [
|
|
164
|
+
{
|
|
165
|
+
scopes: [:fetch],
|
|
166
|
+
message: "'origin.href' is required for remote sources",
|
|
167
|
+
check: lambda { |config|
|
|
168
|
+
if %w[jira github gitlab].include?(config.origin['source']) && config.origin['href'].to_s.empty?
|
|
169
|
+
raise ArgumentError, "'origin.href' is required for remote source '#{config.origin['source']}'"
|
|
170
|
+
end
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
scopes: [:fetch],
|
|
175
|
+
message: "'rhyml' file must exist at origin.href",
|
|
176
|
+
check: lambda { |config|
|
|
177
|
+
if config.origin['source'] == 'rhyml' && !File.exist?(config.origin['href'].to_s)
|
|
178
|
+
raise ArgumentError, "Missing RHYML file at: #{config.origin['href']}"
|
|
179
|
+
end
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
].freeze
|
|
183
|
+
|
|
184
|
+
# Validates the configuration against rules that match the provided scopes.
|
|
185
|
+
#
|
|
186
|
+
# @param config [Configuration] The configuration object to validate.
|
|
187
|
+
# @param scopes [Array<Symbol>] The scopes to validate against.
|
|
188
|
+
def self.validate_config config, *scopes
|
|
189
|
+
scopes.flatten!
|
|
190
|
+
rules = VALIDATION_RULES.select { |rule| rule[:scopes].intersect?(scopes) }
|
|
191
|
+
rules.each { |rule| rule[:check].call(config) }
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Scans built-in and custom mapping files and produces a unique list of known API source names.
|
|
195
|
+
# The result always includes the special entries 'rhyml' and 'git'.
|
|
196
|
+
#
|
|
197
|
+
# @param config [Configuration] The configuration object.
|
|
198
|
+
# @return [Array<String>] A unique list of known API source names.
|
|
199
|
+
def self.known_api_sources config
|
|
200
|
+
builtin_path = File.join(GEM_ROOT_PATH, 'lib/releasehx/rhyml/mappings')
|
|
201
|
+
custom_path = File.expand_path(config.paths['mappings_dir'] || '_mappings')
|
|
202
|
+
|
|
203
|
+
mapping_files = Dir["#{builtin_path}/*.{yml,yaml}", "#{custom_path}/*-api.yml"]
|
|
204
|
+
|
|
205
|
+
base_names = mapping_files.map do |file|
|
|
206
|
+
base = File.basename(file).sub(/\.ya?ml$/, '')
|
|
207
|
+
base.sub('-api', '')
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
base_names = base_names.reject { |name| name == 'verb_past_tenses' }
|
|
211
|
+
|
|
212
|
+
(base_names + %w[rhyml git]).uniq
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Auto-generated by Sourcerer::Builder
|
|
3
|
+
|
|
4
|
+
module ReleaseHx
|
|
5
|
+
ATTRIBUTES = {:globals=>{"attribute-undefined"=>"drop-line", "attribute-missing"=>"skip", "appendix-caption"=>"Appendix", "appendix-refsig"=>"Appendix", "caution-caption"=>"Caution", "chapter-refsig"=>"Chapter", "example-caption"=>"Example", "figure-caption"=>"Figure", "important-caption"=>"Important", "last-update-label"=>"Last updated", "note-caption"=>"Note", "part-refsig"=>"Part", "prewrap"=>"", "sectids"=>"", "section-refsig"=>"Section", "table-caption"=>"Table", "tip-caption"=>"Tip", "toc-placement"=>"macro", "toc-title"=>"Table of Contents", "untitled-label"=>"Untitled", "version-label"=>"Version", "warning-caption"=>"Warning", "notitle"=>"", "docfile"=>"/home/brian/Documents/work/releasehx/README.adoc", "docdir"=>"/home/brian/Documents/work/releasehx", "docfilesuffix"=>".adoc", "docname"=>"README", "embedded"=>"", "asciidoctor"=>"", "asciidoctor-version"=>"2.0.23", "safe-mode-name"=>"unsafe", "safe-mode-unsafe"=>"", "safe-mode-level"=>0, "max-include-depth"=>64, "user-home"=>"/home/brian", "doctype"=>"article", "htmlsyntax"=>"html", "backend-html5-doctype-article"=>"", "doctype-article"=>"", "backend-html5"=>"", "backend"=>"html5", "outfilesuffix"=>".html", "filetype"=>"html", "filetype-html"=>"", "basebackend-html-doctype-article"=>"", "basebackend-html"=>"", "basebackend"=>"html", "stylesdir"=>".", "iconsdir"=>"./images/icons", "localdate"=>"2026-01-01", "localyear"=>"2026", "localtime"=>"13:06:29 -0500", "localdatetime"=>"2026-01-01 13:06:29 -0500", "docdate"=>"2026-01-01", "docyear"=>"2026", "doctime"=>"04:37:26 -0500", "docdatetime"=>"2026-01-01 04:37:26 -0500", "page-layout"=>"default", "page-permalink"=>"/docs", "page-nav_order"=>"1", "doctitle"=>"ReleaseHx", "docopslab_git_www"=>"https://github.com/DocOps", "this_prod_slug"=>"releasehx", "releasehx_prod_repo"=>"https://github.com/DocOps/releasehx", "releasehx_demo_repo"=>"https://github.com/DocOps/releasehx-demo", "this_prod_repo"=>"https://github.com/DocOps/releasehx", "this_prod_vrsn_major"=>"0", "this_prod_vrsn_minor"=>"1", "this_prod_vrsn_major-minor"=>"0.1", "this_prod_vrsn_patch"=>"0", "this_prod_vrsn"=>"0.1.0", "next_prod_vrsn"=>"0.2.0", "tagline"=>"Generate formatted release histories from Jira, GitHub, GitLab, YAML, or JSON sources.", "description"=>"CLI utility and Ruby API for generating structured release notes and changelog documents from various issue-tracking platforms or YAML definitions into plaintext drafts (<strong>AsciiDoc</strong>, <strong>Markdown</strong>, <strong>YAML</strong>) and rich-text output (<strong>HTML</strong> and <strong>PDF</strong>).", "gem_config_definition_path"=>"./specs/data/config-def.yml", "app_default_config_path"=>"./.releasehx.yml", "default_markup"=>"markdown", "default_slug_type"=>"kebab", "default_tplt_lang"=>"liquid", "default_drafts_dir"=>"_drafts", "default_enrich_dir"=>"_publish", "default_output_dir"=>".", "default_payloads_dir"=>"_payloads", "default_templates_dir"=>"_templates", "default_mappings_dir"=>"_mappings", "default_api_clients_dir"=>"_apis", "default_cache_dir"=>".releasehx/cache", "default_api_cred_env"=>"RELEASEHX_API_CRED", "default_api_key_env"=>"RELEASEHX_API_KEY", "default_api_user_env"=>"RELEASEHX_API_USER", "default_api_org_env"=>"RELEASEHX_API_ORG", "markdown_extensions"=>".md, .markdown", "asciidoc_extensions"=>".adoc, .ad, .asciidoc", "yaml_extensions"=>".yml, .yaml, .rhyml", "draft_source_file_types"=>"AsciiDoc, Markdown, YAML", "draft_source_extensions"=>".md, .markdown, .adoc, .ad, .asciidoc, .yml, .yaml, .rhyml", "enrich_file_types"=>"HTML, PDF", "enrich_extensions"=>".html, .pdf", "docker_base_command"=>"docker run -it --rm --user $(id -u):$(id -g) -v $(pwd):/workdir docopslab/releasehx rhx", "this_prod_repo_branch"=>"https://github.com/DocOps/releasehx/tree/release/0.1", "docs_extn"=>"", "toc"=>"", "toclevels"=>"3", "authorcount"=>0, "toc-position"=>"content"}}
|
|
6
|
+
|
|
7
|
+
SNIPPET_LOOKUP = {"helpscreen"=>"helpscreen.txt"}
|
|
8
|
+
|
|
9
|
+
REGION_LOOKUP = {"ai-prompt"=>"releasehx-ai-prompt.adoc"}
|
|
10
|
+
|
|
11
|
+
def self.read_built_snippet name
|
|
12
|
+
fname = SNIPPET_LOOKUP[name.to_s] || name.to_s
|
|
13
|
+
path = File.expand_path("../../../build/snippets/#{fname}", __FILE__)
|
|
14
|
+
raise "Snippet not found: #{name}" unless File.exist?(path)
|
|
15
|
+
File.read(path)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ReleaseHx
|
|
4
|
+
# Utility methods for file format handling and extension mapping.
|
|
5
|
+
# Provides helpers for converting between file extensions and canonical format names.
|
|
6
|
+
|
|
7
|
+
# A map of file extensions to their canonical format names.
|
|
8
|
+
# Used internally by format detection and extension resolution methods.
|
|
9
|
+
#
|
|
10
|
+
# @return [Hash<String, String>] Extension-to-format mapping.
|
|
11
|
+
#
|
|
12
|
+
# TODO: We should externalize this into a YAML file for easier docs integration.
|
|
13
|
+
# Maybe even something that goes in Sourcerer or somewhere to universalize these.
|
|
14
|
+
FORMAT_MAP = {
|
|
15
|
+
'md' => 'markdown',
|
|
16
|
+
'mkd' => 'markdown',
|
|
17
|
+
'mkdn' => 'markdown',
|
|
18
|
+
'mdown' => 'markdown',
|
|
19
|
+
'markdown' => 'markdown',
|
|
20
|
+
'ad' => 'asciidoc',
|
|
21
|
+
'adoc' => 'asciidoc',
|
|
22
|
+
'asciidoc' => 'asciidoc',
|
|
23
|
+
'yaml' => 'yaml',
|
|
24
|
+
'yml' => 'yaml',
|
|
25
|
+
'json' => 'json',
|
|
26
|
+
'pdf' => 'pdf',
|
|
27
|
+
'html' => 'html'
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
# Resolves the user's preferred file extension for a given format.
|
|
31
|
+
# Consults the configuration's extension preferences and falls back to format detection.
|
|
32
|
+
#
|
|
33
|
+
# @param string [String] The format or extension to look up.
|
|
34
|
+
# @param config [Hash] The configuration hash containing extension preferences.
|
|
35
|
+
# @return [String] The preferred file extension.
|
|
36
|
+
def self.format_extension string, config
|
|
37
|
+
return string unless config.is_a?(Hash) && config.key?('extensions')
|
|
38
|
+
|
|
39
|
+
format = file_format_id(string)
|
|
40
|
+
return string.downcase unless format
|
|
41
|
+
|
|
42
|
+
config.dig('extensions', format) || string.downcase
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Converts a file extension or format name to its canonical format identifier.
|
|
46
|
+
# Used to normalize various extension formats (e.g., 'md', 'mkd', 'markdown') to a standard ID.
|
|
47
|
+
#
|
|
48
|
+
# @param key [String] The format or extension to look up.
|
|
49
|
+
# @return [String, nil] The canonical format ID, or nil if not recognized.
|
|
50
|
+
def self.file_format_id key
|
|
51
|
+
return nil if key.nil?
|
|
52
|
+
|
|
53
|
+
string = key.to_s.strip
|
|
54
|
+
return nil if string.empty?
|
|
55
|
+
|
|
56
|
+
FORMAT_MAP[string.downcase]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../docopslab/mcp/asset_packager'
|
|
4
|
+
require_relative 'manifest'
|
|
5
|
+
|
|
6
|
+
module ReleaseHx
|
|
7
|
+
module MCP
|
|
8
|
+
# Copies MCP resource assets into a packaged location for runtime access.
|
|
9
|
+
class AssetPackager < DocOpsLab::MCP::AssetPackager
|
|
10
|
+
def initialize manifest: Manifest.load, asset_root: default_asset_root
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def default_asset_root
|
|
17
|
+
File.expand_path('assets', __dir__)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# ReleaseHx configuration: agent guide (MVP)
|
|
2
|
+
|
|
3
|
+
This server exposes several authoritative artifacts for configuration work:
|
|
4
|
+
|
|
5
|
+
- `releasehx://agent/guide` (this file): how to use the two files above effectively
|
|
6
|
+
- `releasehx://config/sample` (YAML): the best map of the entire config tree (defaults + comments + commented-out optional keys)
|
|
7
|
+
- `releasehx://config/schema` (YAML): the authoritative definition (types, constraints, long docs)
|
|
8
|
+
- `releasehx://config/reference.json` (JSON): a queryable version of the complete config-reference docs
|
|
9
|
+
- tool `config.reference.get` (accepts: JSON Pointer string; returns JSON object)
|
|
10
|
+
- `releasehx://config/reference.adoc` (AsciiDoc) the formatted version of the reference output.
|
|
11
|
+
|
|
12
|
+
## DSL Notice
|
|
13
|
+
|
|
14
|
+
You are interacting with a YAML extension called SGYML, with its own ways of referencing YAML nodes and data types. It may be worth being able to "translate" some of this terminology or tailor it to your Operator's needs or preferences.
|
|
15
|
+
|
|
16
|
+
### Glossary
|
|
17
|
+
|
|
18
|
+
**object**: Any instance of keyed data in the YAML file.
|
|
19
|
+
**Map**: A "mapping" in YAML or "object" in JSON.
|
|
20
|
+
**Array**: A sequence in YAML, "array" in most languages.
|
|
21
|
+
**ArrayTable**: A sequence of sibling Maps with expected parameters.
|
|
22
|
+
|
|
23
|
+
### Key Tip: Distinguish *declared* keys from *arbitrary* keys
|
|
24
|
+
|
|
25
|
+
ReleaseHx has both:
|
|
26
|
+
|
|
27
|
+
**Declared keys** (fixed, “standard” properties):
|
|
28
|
+
These are explicitly defined in the schema/sample tree. Do not make up new *declared* keys.
|
|
29
|
+
|
|
30
|
+
**Arbitrary keys** (user-defined names inside certain maps):
|
|
31
|
+
Some objects are intentionally “free-key” dictionaries where the user supplies their own key names (for example: named type definitions, named tag definitions, named link templates, etc). In those places, inventing keys is correct and expected.
|
|
32
|
+
|
|
33
|
+
How to tell which you’re in:
|
|
34
|
+
|
|
35
|
+
- If the sample tree shows placeholder keys like `<name>`, `<type_name>`, `<id>`, or similar “template keys,” treat the *key name* as arbitrary and user-defined.
|
|
36
|
+
- If the sample tree shows a concrete key name (e.g., `origin`, `href`, `mode`) treat it as declared and do not invent siblings that aren’t present.
|
|
37
|
+
- When in doubt, consult `releasehx://config/schema` for the node’s semantics (look for wording indicating “map of … keyed by …”, wildcard keys, or examples using placeholders).
|
|
38
|
+
|
|
39
|
+
## What You Can Help With, Agent
|
|
40
|
+
|
|
41
|
+
Use these assets for any of the following scenarios:
|
|
42
|
+
|
|
43
|
+
### Scenario A: Create a new config
|
|
44
|
+
- Generate a minimal `.releasehx.yml` that sets only what the user needs.
|
|
45
|
+
- Start from the sample tree to avoid missing required structure.
|
|
46
|
+
- Keep changes small: only override what must differ from defaults.
|
|
47
|
+
- If the user needs a named item (type/tag/link/etc), create it under the appropriate “arbitrary keys” map using a sensible name.
|
|
48
|
+
|
|
49
|
+
### Scenario B: Modify an existing config
|
|
50
|
+
- Read the user’s current config.
|
|
51
|
+
- Compare the affected subtree against the sample tree.
|
|
52
|
+
- Make the smallest possible edit (avoid restructuring unrelated blocks).
|
|
53
|
+
- If the change involves a named entry (arbitrary key map), add/update only that one entry.
|
|
54
|
+
|
|
55
|
+
### Scenario C: Troubleshoot behavior
|
|
56
|
+
- When output/content is surprising, locate the relevant top-level block in the sample tree first.
|
|
57
|
+
- Use the schema definition to confirm allowed values and semantics before proposing changes.
|
|
58
|
+
- Prefer quoting the schema’s constraints/intent rather than guessing.
|
|
59
|
+
- If behavior depends on a named entry (type/tag/link/etc), confirm the user’s key name matches the referenced name elsewhere.
|
|
60
|
+
|
|
61
|
+
### Scenario D: Discover available knobs
|
|
62
|
+
- Use the sample config as a TOC.
|
|
63
|
+
- If you need deeper meaning, consult the schema entry for that subtree.
|
|
64
|
+
- If the user asks for “all options,” point them to the sample tree and offer to focus on one subtree at a time.
|
|
65
|
+
|
|
66
|
+
## Where config comes from
|
|
67
|
+
|
|
68
|
+
- Defaults are defined by the schema definition (`releasehx://config/schema`).
|
|
69
|
+
- Users override defaults via `.releasehx.yml` or a `--config <path>` flag.
|
|
70
|
+
|
|
71
|
+
## How to navigate quickly (recommended method)
|
|
72
|
+
|
|
73
|
+
1) Load `releasehx://config/sample` and use it as the “table of contents.”
|
|
74
|
+
2) Identify the smallest subtree relevant to the user’s goal.
|
|
75
|
+
3) Decide whether you are working with:
|
|
76
|
+
- a declared-key subtree (fixed property names), or
|
|
77
|
+
- an arbitrary-key map (user-defined item names).
|
|
78
|
+
4) Only if needed, consult `releasehx://config/schema` for:
|
|
79
|
+
- allowed values (enums)
|
|
80
|
+
- types (string/boolean/integer/uri/path/template/etc)
|
|
81
|
+
- templating rules and any special behaviors
|
|
82
|
+
5) Apply minimal edits to the user’s config.
|
|
83
|
+
|
|
84
|
+
## Hack for inspecting the effective config
|
|
85
|
+
|
|
86
|
+
Append the following to any proper `rhx` command to write an "effective config" to file.
|
|
87
|
+
|
|
88
|
+
```shell
|
|
89
|
+
--debug 2>&1 | awk '
|
|
90
|
+
/^DEBUG: Operative config settings:/ {p=1; next}
|
|
91
|
+
p && /^---[[:space:]]*$/ {y=1}
|
|
92
|
+
y {print}
|
|
93
|
+
y && /^[[:space:]]*$/ {exit}
|
|
94
|
+
' > .agent/tmp/effective-config.yml
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Replace the `> PATH` with one appropriate to your Operator's preferences or local application structure.
|
|
98
|
+
|
|
99
|
+
## Counter-prompts (questions to ask the Operator)
|
|
100
|
+
|
|
101
|
+
Ask only what you need to select the right subtree and values:
|
|
102
|
+
|
|
103
|
+
- What is the issues origin source? (Jira / GitHub / GitLab / RHYML file)
|
|
104
|
+
- What is the origin endpoint or file location?
|
|
105
|
+
- Do you want Markdown or AsciiDoc output? (and do you want frontmatter?)
|
|
106
|
+
- Are you generating drafts, final outputs, or both?
|
|
107
|
+
- Do you want to group/sort by type/part/tags, or keep defaults?
|
|
108
|
+
- Are credentials provided via environment variables, or do you need explicit auth config?
|
|
109
|
+
- If you’re defining named items (types/tags/links/etc), what names do you want to use for those entries?
|
|
110
|
+
|
|
111
|
+
If the user provides only a vague goal, ask one question at a time until you can pick the right subtree.
|
|
112
|
+
|
|
113
|
+
## Common top-level blocks (use the sample tree as the source of truth)
|
|
114
|
+
|
|
115
|
+
Typical blocks include (names may vary; consult the sample tree):
|
|
116
|
+
|
|
117
|
+
- `$meta` (markup + conventions)
|
|
118
|
+
- `origin` (API/file source + auth + project identifiers)
|
|
119
|
+
- `conversions` (how summaries/heads/notes are derived)
|
|
120
|
+
- `extensions` (file extension settings)
|
|
121
|
+
- `types`, `parts`, `tags` (classification/grouping controls; often include arbitrary keys)
|
|
122
|
+
- `links` (templated link formats; often include arbitrary keys)
|
|
123
|
+
- `paths` (input/output locations)
|
|
124
|
+
- `modes` (execution defaults)
|
|
125
|
+
- `rhyml` (RHYML-specific behaviors)
|
|
126
|
+
|
|
127
|
+
## When to consult schema (instead of sample)
|
|
128
|
+
|
|
129
|
+
Use `releasehx://config/schema` when:
|
|
130
|
+
|
|
131
|
+
- the user’s desired value is not obvious from comments
|
|
132
|
+
- you need to confirm an enum (e.g., `origin.source` values)
|
|
133
|
+
- a property is templated and you need to understand substitution variables
|
|
134
|
+
- you suspect a type mismatch (URI vs Path vs String, etc)
|
|
135
|
+
- you need to confirm whether a subtree allows arbitrary keys, and what each entry’s structure must be
|
|
136
|
+
|
|
137
|
+
## Potentially unintuitive principles
|
|
138
|
+
|
|
139
|
+
- **“Frontmatter” is controlled in two places (toggle vs template).**
|
|
140
|
+
If you’re trying to *turn frontmatter on/off*, look under `modes.*_frontmatter`. If you’re trying to *change what frontmatter contains*, look under `templates.*_frontmatter` (Markdown/AsciiDoc/HTML each have their own). Also note `modes.wrapped` affects HTML wrapping, which can look like a frontmatter problem when it’s really wrapping/boilerplate.
|
|
141
|
+
|
|
142
|
+
- **Links require both a template and an enable switch.**
|
|
143
|
+
Defining URL templates under `links.web.href` / `links.git.href` does nothing by itself. You must also enable display under `history.items.show_issue_links` / `history.items.show_git_links` (or the per-section overrides under changelog/notes items, if present). If a user says “I set the link template but no links appear,” check the `history.*items*` flags.
|
|
144
|
+
|
|
145
|
+
- **There are multiple “markup” concepts.**
|
|
146
|
+
`$meta.markup` is the markup format used inside *config strings* (and is meant to keep defaults cross-compatible). `rhyml.markup` is the markup for the *RHYML note/memo content* and can also be overridden inside RHYML documents themselves. If a user complains “my Markdown isn’t converting to AsciiDoc,” don’t only look at `$meta`.
|
|
147
|
+
|
|
148
|
+
- **API source selection is under `origin`, but API customization lives under `paths`.**
|
|
149
|
+
`origin.source` selects the API type (`jira`, `github`, `gitlab`, `rhyml`) and `origin.href` points at the endpoint or payload location. But “where do I put custom mappings/clients?” is not in `origin.*`; it’s in `paths.mappings_dir` and `paths.api_clients_dir`, and those directories are searched using filenames derived from `origin.source`.
|
|
150
|
+
|
|
151
|
+
- **`origin.href` can be templated, and its variables come from siblings and CLI args.**
|
|
152
|
+
If the endpoint/path needs to vary by project or version, `origin.href` supports templating (for example, using the sibling `origin.project` and the argued release version). If a user expects `{{ version }}` or project placeholders to work and they don’t, treat it as a templating/evaluation-order issue, not a path issue.
|
|
153
|
+
|
|
154
|
+
- **“How do I change what becomes `summ` / `head` / note text?” is under `conversions`, not templates.**
|
|
155
|
+
The `conversions` block is about *where content originates* (issue title vs custom field vs commit message, etc) and how it maps into RHYML fields. If the user asks “use a custom field for summaries,” don’t go hunting in output templates first.
|
|
156
|
+
|
|
157
|
+
- **Classification maps are intentionally “invent-your-own-key” dictionaries.**
|
|
158
|
+
Blocks like `types`, `parts`, and (parts of) `tags` are designed so users define named entries. If you see placeholder keys like `<type_name>` or `<part_name>` or language implying “arbitrarily named,” that means the *map keys are user-defined identifiers* and should be invented to match the user’s taxonomy or existing labels.
|
|
159
|
+
|
|
160
|
+
- **Tags are both a filter and a dictionary of tag definitions.**
|
|
161
|
+
The tags system is easy to misread:
|
|
162
|
+
- Some keys (like `_include` / `_exclude`) behave like filter controls for *including* or *excluding* entire issue/change records at RHYML conversion time.
|
|
163
|
+
- Many other keys represent *individual tags* that may be imported from the issue system or assigned in RHYML.
|
|
164
|
+
- Only tags that are declared in the tags block will typically be preserved/ported from API payload into RHYML tags.
|
|
165
|
+
If a user says “my label exists in GitHub but it’s not showing up,” it’s usually because it isn’t declared under `tags`.
|
|
166
|
+
|
|
167
|
+
- **Some tags are “operational” and default to being dropped.**
|
|
168
|
+
A tag can exist only to drive inclusion (example: `changelog`) and may default to `drop: true`, meaning it influences selection but is not shown in published output unless explicitly configured otherwise. If the user expects to see it in output, check the tag’s `drop` and `groupable` settings.
|
|
169
|
+
|
|
170
|
+
- **Output location is split into base dir, subdirs, and filename templates.**
|
|
171
|
+
If a user says “ReleaseHx is writing to the wrong folder,” check:
|
|
172
|
+
- `paths.output_dir` (the base)
|
|
173
|
+
- `paths.drafts_dir` and `paths.enrich_dir` (subdirectories)
|
|
174
|
+
- `paths.*_filename` templates (which may include `{{ version }}` and `{{ format_ext }}` and are resolved late).
|
|
175
|
+
If file extensions look wrong, it may be an `extensions.*` issue, not a `paths.*` issue, because `format_ext` comes from extension preferences.
|
|
176
|
+
|
|
177
|
+
- **Cache settings live under `paths.cache` (not under origin or modes).**
|
|
178
|
+
If a user wants fewer API calls or wonders “why am I seeing cached payload behavior,” look under `paths.cache.*` (enabled/ttl/dir) and related gitignore prompting. This often presents like an origin/API issue but isn’t.
|