jekyll-documents 0.7.2 → 0.7.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 884c3afc18e96312dcd25a2328f652ec8cb1417ad8ec26337b4aa4ee2f175517
4
- data.tar.gz: 29e0d75b6b50c68acb124e0b31123cc85c3810789c39d562beb8da00c5c50a87
3
+ metadata.gz: aa6c0e2569d3772c02fe174700001a8290d7b313e7b068781d21627172d9e785
4
+ data.tar.gz: 407cc042bf5b2de1fe1b01cf9c9043bd961c811de7d0e6ee2dc896b1ab29e1dc
5
5
  SHA512:
6
- metadata.gz: c50ee45d94b33d62b344dadda31c9cf865d8d175de1b00e7c7f92aaeb046f0cef7fec3835de627e68ec0ce9849d925b8fbacb3012be5a5face435822c173b9c3
7
- data.tar.gz: 3a28dee0ff0b8078cac42e4097676a991496e8510a38518bd7d6ea013275f2f9f804b15b5f4ab46e846f6ab2a5eaa8e72dfd4a535ed8a1b9c7cf338671a64019
6
+ metadata.gz: d9016ce9608b013d8b0b6ac4d3756512d99a17cb55d5c2d2d67e096ccb5175e07ca40a17c68629dd09c684b5291fcad0ea1b876d74e7e58b0fedff0a7d1b97c7
7
+ data.tar.gz: f13021dccbed117f29e6c3f2c3d70f9ba9c499f3008ba30083f69c710ba3b30753a72009e3b84fa7fed64c3c73e8a85d20630cbe26de46623a38fb7766cd3986
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.3] - 2026-09-10
4
+
5
+ ### Added
6
+ - Added Semgrep CE security scanning with a custom ReDoS detection rule for pre-commit and CI
7
+ - Added CodeFactor code quality analysis badge and integration
8
+ - Added SEMGREP_APP_TOKEN integration for Cloud dashboard sync from CI
9
+ - Added `rake version:pre_release` task for pre-release security gate with FORCE=1 override
10
+
11
+ ### Changed
12
+ - Updated CI to Node.js 26 and current Node 24-based GitHub Actions runtimes
13
+ - Pinned all GitHub Actions to commit SHAs to prevent supply-chain attacks
14
+ - Added cooldown period to Dependabot configuration
15
+ - Pinned Lunr.js CDN to version 2.3.9 with SRI integrity hash
16
+ - Bumped actions/checkout to v7.0.1 (SHA-pinned)
17
+ - Reduced method complexity across 6 flagged CodeFactor findings:
18
+ - `generator.rb`: Extracted `process_file` and `merge_passthrough_fields` from `generate` and `configure_client_search`
19
+ - `doc_link.rb`: Extracted `match_documents` and `report_ambiguous` from `find_document`
20
+ - `doc_category.rb`: Extracted `match_categories` and `report_ambiguous_category` from `resolve_category`
21
+ - `latest_documents.rb`: Extracted `render_list` and `list_item` from `render`
22
+ - `options_parser.rb`: Extracted `read_value` from `parse_options`
23
+
24
+ ### Fixed
25
+ - Replaced dangerous subshell in system spec with Open3.capture2
26
+
3
27
  ## [0.7.2] - 2026-09-09
4
28
 
5
29
  ### Fixed
data/README.md CHANGED
@@ -6,6 +6,8 @@
6
6
  [![Gem Downloads](https://img.shields.io/gem/dt/jekyll-documents)](https://rubygems.org/gems/jekyll-documents)
7
7
  [![License: AGPL-3.0-only](https://img.shields.io/badge/license-AGPL--3.0--only-blue)](LICENSE)
8
8
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/gundestrup/jekyll-documents)
9
+ [![CodeFactor](https://www.codefactor.io/repository/github/gundestrup/jekyll-documents/badge)](https://www.codefactor.io/repository/github/gundestrup/jekyll-documents)
10
+ [![Semgrep CE](https://img.shields.io/badge/Semgrep_CE-security-success)](https://github.com/gundestrup/jekyll-documents/security/code-scanning)
9
11
 
10
12
  Turn files in `assets/documents/` into browsable document pages.
11
13
 
@@ -9,6 +9,8 @@
9
9
  window.__jekyllDocumentsIndexPath = {{ index_path | relative_url | jsonify }};
10
10
  window.__jekyllDocumentsBaseurl = {{ site.baseurl | jsonify }};
11
11
  </script>
12
- <script src="https://unpkg.com/lunr/lunr.js"></script>
12
+ <script src="https://unpkg.com/lunr@2.3.9/lunr.js"
13
+ integrity="sha384-Mrc0XsDxndfJgV5jLnEeThch4j9dNEXAE1vbWeyzmERxSvF8siQfuEczzgUelQQH"
14
+ crossorigin="anonymous"></script>
13
15
  <script src="{{ '/assets/js/documents-search.js' | relative_url }}"></script>
14
16
  {% endif %}
@@ -43,39 +43,10 @@ module Jekyll
43
43
  Dir.glob("#{root}/**/*").each do |path|
44
44
  next unless File.file?(path)
45
45
 
46
- source_extension = File.extname(path)
47
- ext = source_extension.downcase
48
- if @config["strict_extensions"] && !@config["include_extensions"].include?(ext)
49
- ::Jekyll.logger.abort_with "jekyll-documents",
50
- "Unsupported file type: #{path} (#{ext})"
51
- end
52
- next unless @config["include_extensions"].include?(ext)
46
+ doc, rel_path = process_file(path, root, collection)
47
+ next unless doc
53
48
 
54
- source_path = source_path_for(path, root)
55
- category_path = document_category_path(source_path)
56
- category = remap_category(category_path)
57
- rel_path = normalize_path(File.join(@config["root"], source_path))
58
49
  current_paths << rel_path
59
- basename = File.basename(path, source_extension)
60
-
61
- date, title, valid = parse_filename(basename)
62
- if !valid && @config["strict_filename"]
63
- ::Jekyll.logger.abort_with "jekyll-documents",
64
- "Filename must be 'YYYY-MM-DD_Title.ext' → #{rel_path}"
65
- end
66
-
67
- doc = ::Jekyll::Document.new(
68
- source_stub_for(source_path),
69
- site: site,
70
- collection: collection
71
- )
72
- file_info = {
73
- title: title, date: date, category: category, category_path: category_path,
74
- category_slug: slugify(category, "uncategorized"), source_path: source_path,
75
- rel_path: rel_path, ext: ext, file_type: ext.delete_prefix("."),
76
- icon_set: @config["icon_set"], slug: build_slug(basename), path: path
77
- }
78
- bake_document_data(doc, file_info)
79
50
  collection.docs << doc
80
51
  generated_docs << doc
81
52
  end
@@ -87,6 +58,42 @@ module Jekyll
87
58
 
88
59
  private
89
60
 
61
+ def process_file(path, root, collection)
62
+ source_extension = File.extname(path)
63
+ ext = source_extension.downcase
64
+ if @config["strict_extensions"] && !@config["include_extensions"].include?(ext)
65
+ ::Jekyll.logger.abort_with "jekyll-documents",
66
+ "Unsupported file type: #{path} (#{ext})"
67
+ end
68
+ return [nil, nil] unless @config["include_extensions"].include?(ext)
69
+
70
+ source_path = source_path_for(path, root)
71
+ rel_path = normalize_path(File.join(@config["root"], source_path))
72
+ basename = File.basename(path, source_extension)
73
+
74
+ date, title, valid = parse_filename(basename)
75
+ if !valid && @config["strict_filename"]
76
+ ::Jekyll.logger.abort_with "jekyll-documents",
77
+ "Filename must be 'YYYY-MM-DD_Title.ext' → #{rel_path}"
78
+ end
79
+
80
+ category_path = document_category_path(source_path)
81
+ category = remap_category(category_path)
82
+ doc = ::Jekyll::Document.new(
83
+ source_stub_for(source_path),
84
+ site: @site,
85
+ collection: collection
86
+ )
87
+ file_info = {
88
+ title: title, date: date, category: category, category_path: category_path,
89
+ category_slug: slugify(category, "uncategorized"), source_path: source_path,
90
+ rel_path: rel_path, ext: ext, file_type: ext.delete_prefix("."),
91
+ icon_set: @config["icon_set"], slug: build_slug(basename), path: path
92
+ }
93
+ bake_document_data(doc, file_info)
94
+ [doc, rel_path]
95
+ end
96
+
90
97
  # Auto-injects passthrough_fields into client_search config when the
91
98
  # documents collection is indexed. Only acts if client_search is already
92
99
  # configured with +documents+ in its collections list — does nothing if
@@ -96,13 +103,17 @@ module Jekyll
96
103
  return unless search_config.is_a?(Hash)
97
104
  return unless Array(search_config["collections"]).include?("documents")
98
105
 
106
+ merge_passthrough_fields(search_config)
107
+ search_config["icon_field"] = "icon_url" unless search_config.key?("icon_field")
108
+ end
109
+
110
+ def merge_passthrough_fields(search_config)
99
111
  fields = search_config["passthrough_fields"] || []
100
112
  existing = fields.flat_map { |f| f.is_a?(Hash) ? f.keys : [f.to_s] }
101
113
  %w[file_type icon_url icon_set].each do |field|
102
114
  fields << field unless existing.include?(field)
103
115
  end
104
116
  search_config["passthrough_fields"] = fields
105
- search_config["icon_field"] = "icon_url" unless search_config.key?("icon_field")
106
117
  end
107
118
 
108
119
  # Ensures a collection exists and is configured for output
@@ -27,17 +27,21 @@ module Jekyll
27
27
  scanner.skip(/\s*/)
28
28
  next unless scanner.scan(":")
29
29
 
30
- scanner.skip(/\s*/)
31
- value = if scanner.scan(/'([^']*)'/) || scanner.scan(/"([^"]*)"/)
32
- scanner[1]
33
- else
34
- scanner.scan(/\S+/)
35
- end
30
+ value = read_value(scanner)
36
31
  options[key] = value unless value.nil?
37
32
  end
38
33
 
39
34
  options
40
35
  end
36
+
37
+ def read_value(scanner)
38
+ scanner.skip(/\s*/)
39
+ if scanner.scan(/'([^']*)'/) || scanner.scan(/"([^"]*)"/)
40
+ scanner[1]
41
+ else
42
+ scanner.scan(/\S+/)
43
+ end
44
+ end
41
45
  end
42
46
  end
43
47
  end
@@ -13,18 +13,24 @@ module Jekyll
13
13
  normalized = query.to_s.strip.downcase
14
14
  return nil if normalized.empty?
15
15
 
16
- exact = categories.select do |category|
17
- category_values(category).include?(normalized)
18
- end
19
- matches = exact.empty? ? partial_categories(categories, normalized) : exact
16
+ matches = match_categories(categories, normalized)
20
17
  aggregate = options["aggregate"] == "true" && options["list"] == "true"
21
18
  return matches if matches.one? || (aggregate && matches.any?)
22
19
  return nil if matches.empty?
23
20
 
21
+ report_ambiguous_category(site, query, matches)
22
+ nil
23
+ end
24
+
25
+ def match_categories(categories, normalized)
26
+ exact = categories.select { |cat| category_values(cat).include?(normalized) }
27
+ exact.empty? ? partial_categories(categories, normalized) : exact
28
+ end
29
+
30
+ def report_ambiguous_category(site, query, matches)
24
31
  paths = matches.map { |category| category["path"] }.sort.join(", ")
25
32
  message = "Ambiguous doc_category #{query.inspect}; matches: #{paths}. Rendering nothing."
26
33
  report_resolution_issue(site, message)
27
- nil
28
34
  end
29
35
 
30
36
  def resolve_category_path(path, categories, site)
@@ -13,21 +13,27 @@ module Jekyll
13
13
  normalized = query.to_s.strip.downcase
14
14
  return nil if normalized.empty?
15
15
 
16
- exact = docs.select { |doc| document_values(doc).include?(normalized) }
17
- matches = if exact.empty?
18
- docs.select do |doc|
19
- document_values(doc).any? { |value| value.include?(normalized) }
20
- end
21
- else
22
- exact
23
- end
16
+ matches = match_documents(docs, normalized)
24
17
  return matches.first if matches.one?
25
18
  return nil if matches.empty?
26
19
 
20
+ report_ambiguous(site, query, matches)
21
+ nil
22
+ end
23
+
24
+ def match_documents(docs, normalized)
25
+ exact = docs.select { |doc| document_values(doc).include?(normalized) }
26
+ return exact unless exact.empty?
27
+
28
+ docs.select do |doc|
29
+ document_values(doc).any? { |value| value.include?(normalized) }
30
+ end
31
+ end
32
+
33
+ def report_ambiguous(site, query, matches)
27
34
  paths = matches.map { |doc| doc.data["source_path"] || doc.path }.sort.join(", ")
28
35
  message = "Ambiguous doc_link #{query.inspect}; matches: #{paths}. Rendering nothing."
29
36
  report_resolution_issue(site, message)
30
- nil
31
37
  end
32
38
 
33
39
  def find_document_by_path(docs, path, site)
@@ -20,19 +20,25 @@ module Jekyll
20
20
  docs = docs.select { |doc| doc.data["category"] == category } if category
21
21
  docs = docs.sort_by { |doc| doc.data["date"] || Time.at(0) }.reverse.first(count)
22
22
 
23
+ render_list(docs)
24
+ end
25
+
26
+ private
27
+
28
+ def render_list(docs)
23
29
  out = +"<ul class=\"latest-documents\">\n"
24
- docs.each do |doc|
25
- data = doc.data
26
- title = escape_html(data["title"])
27
- url = escape_html(doc.url)
28
- date = (data["date"] || Time.at(0)).strftime("%Y-%m-%d")
29
- out << %(<li><a href="#{url}">#{title}</a> <small>(#{date})</small></li>\n)
30
- end
30
+ docs.each { |doc| out << list_item(doc) }
31
31
  out << "</ul>\n"
32
32
  out
33
33
  end
34
34
 
35
- private
35
+ def list_item(doc)
36
+ data = doc.data
37
+ title = escape_html(data["title"])
38
+ url = escape_html(doc.url)
39
+ date = (data["date"] || Time.at(0)).strftime("%Y-%m-%d")
40
+ %(<li><a href="#{url}">#{title}</a> <small>(#{date})</small></li>\n)
41
+ end
36
42
 
37
43
  def escape_html(text)
38
44
  return "" unless text
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Jekyll
4
4
  module Documents
5
- VERSION = "0.7.2"
5
+ VERSION = "0.7.3"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-documents
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svend Gundestrup