bridgetown_directus 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd3625cab0b39f5374ad7a3647e5b6e369bed783f05412cb38e15e8f10208908
4
- data.tar.gz: a99f097216b2c9239188563a288b0df5752709782c29bfe15673edd73fd6c80d
3
+ metadata.gz: 6834fa39c0868873782728053787ca9fbcbd0defb231b70621c8737ba6e996ee
4
+ data.tar.gz: 9dfb896e01a1a7e48d0a6db38b41c12a092195650ea8945083b417d34a76bda6
5
5
  SHA512:
6
- metadata.gz: d70b609eab2f9c4015c4e8898324fe046f8a62673c51d11057ec166372cceb7dfc3cba9793b1a37e3dd175f150d1d2dbc2c7c371427e740b61303630228793f0
7
- data.tar.gz: 1dafe4be86342c4196ca93700035ad75f99b0a6a7d0ab78f0592f8ec0a76e51a223b7b63a7f46bf1b0568593b8322b0428f95e5329f38788c55d0a9b07907a9a
6
+ metadata.gz: e2394537291cd18e7887344e9d37c21973eafc19ec2b89dfc38d7c6796011ac8d22ca4777bacf052cf5f4e5cd2ba1dd26019a897cf0a0d26a901fec4aaafba5e
7
+ data.tar.gz: a72244a4b1bf68e1e4d8834c5c0548c4a2a3287267a040f3133b5b5cca2a67432948adbc8c0abaf2bb577d852023300aa7dd80c765c374e8c17f72771ed63625
@@ -0,0 +1,23 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ test:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: "3.4"
21
+ bundler-cache: true
22
+ - name: Run tests
23
+ run: bundle exec rake test
@@ -0,0 +1,20 @@
1
+ name: Release Please
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: write
11
+ pull-requests: write
12
+
13
+ jobs:
14
+ release-please:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: googleapis/release-please-action@v4
18
+ with:
19
+ config-file: .release-please-config.json
20
+ manifest-file: .release-please-manifest.json
@@ -0,0 +1,28 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: "3.4"
20
+ bundler-cache: true
21
+ - name: Run tests
22
+ run: bundle exec rake test
23
+ - name: Build gem
24
+ run: bundle exec rake build
25
+ - name: Push gem
26
+ env:
27
+ GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
28
+ run: gem push pkg/*.gem
data/.gitignore CHANGED
@@ -38,3 +38,6 @@ test/dest
38
38
  .bridgetown-webpack
39
39
 
40
40
  .env
41
+
42
+ # MacOS
43
+ .DS_Store
@@ -0,0 +1,10 @@
1
+ {
2
+ "packages": {
3
+ ".": {
4
+ "release-type": "ruby",
5
+ "package-name": "bridgetown_directus",
6
+ "changelog-path": "CHANGELOG.md",
7
+ "include-v-in-tag": true
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.3.0"
3
+ }
data/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.0](https://github.com/Munkun-Estudio/bridgetown_directus/compare/bridgetown_directus-v0.2.0...bridgetown_directus/v0.3.0) (2026-01-27)
9
+
10
+
11
+ ### Features
12
+
13
+ * improve builder mapping and release automation ([b87c719](https://github.com/Munkun-Estudio/bridgetown_directus/commit/b87c719cfec175a86bc1f9388c308c496035d2cf))
14
+
8
15
  ## [Unreleased]
9
16
 
10
17
  - ...
data/README.md CHANGED
@@ -26,7 +26,7 @@ Before installing the plugin, make sure you have an [Auth Token](https://docs.di
26
26
 
27
27
  This will:
28
28
  - Prompt for your Directus API URL, token, Directus collection name, and Bridgetown collection name
29
- - Generate a minimal `config/initializers/bridgetown_directus.rb`
29
+ - Generate a minimal `config/initializers.rb`
30
30
  - All further customization is done in Ruby, not YAML
31
31
 
32
32
  ### Manual Installation
@@ -38,7 +38,7 @@ Before installing the plugin, make sure you have an [Auth Token](https://docs.di
38
38
  ```
39
39
 
40
40
  2. Run `bundle install` to install the gem.
41
- 3. Create `config/initializers/bridgetown_directus.rb` (see below for configuration).
41
+ 3. Create `config/initializers.rb` (see below for configuration).
42
42
 
43
43
  ## Configuration
44
44
 
@@ -66,10 +66,19 @@ For custom collections, create a layout file at `src/_layouts/[singular].erb` (e
66
66
 
67
67
  **By default, all Directus fields will be written to the front matter of generated Markdown files.**
68
68
  You only need to declare fields with `c.field` if you want to:
69
+
69
70
  - Rename a field in the output
70
71
  - Transform/convert a field value (e.g., format a date, generate a slug, etc.)
71
72
  - Set a default value if a field is missing
72
73
 
74
+ Mapped fields are merged into the full Directus payload, so you still retain access to original fields unless you override them.
75
+
76
+ ### Environment Variables
77
+
78
+ - `DIRECTUS_API_URL` (required unless you set `directus.api_url` in config)
79
+ - `DIRECTUS_API_TOKEN` (required unless you set `directus.token` in config)
80
+ - `DIRECTUS_TOKEN` (legacy fallback; supported for backward compatibility)
81
+
73
82
  #### Example: Customizing a Field
74
83
 
75
84
  ```ruby
@@ -93,10 +102,16 @@ c.enable_translations([:title, :content])
93
102
 
94
103
  - **Generated files**: The plugin writes Markdown files to `src/_[bridgetown_collection]/` (e.g., `src/_staff_members/`).
95
104
  - **Safety**: Only files with the `directus_generated: true` flag in their front matter are deleted during cleanup. User-authored files are never removed.
105
+ - **Posts**: If `date` or `published_at` is present, filenames are generated as `YYYY-MM-DD-slug.md` to preserve date-based permalinks.
106
+
107
+ ### Debug Logging
108
+
109
+ Set `BRIDGETOWN_DIRECTUS_LOG=1` to print per-collection activity logs during builds.
96
110
 
97
111
  ### Advanced Configuration
98
112
 
99
113
  See the plugin source and inline documentation for advanced features such as:
114
+
100
115
  - Multiple collections
101
116
  - Custom layouts per collection
102
117
  - Filtering, sorting, and pagination via `c.default_query` (**experimental**; not fully tested in production—see notes below)
@@ -106,7 +121,7 @@ See the plugin source and inline documentation for advanced features such as:
106
121
 
107
122
  ### Migrating from 0.1.x
108
123
 
109
- - **YAML config is no longer used.** All configuration is now in Ruby in `config/initializers/bridgetown_directus.rb`.
124
+ - **YAML config is no longer used.** All configuration is now in Ruby in `config/initializers.rb`.
110
125
  - Field mapping, transformation, and translations are handled in the initializer.
111
126
  - All Directus fields are output by default; use `c.field` for customization.
112
127
  - **Upgrading?** The `resource_type` option is no longer required. Use the Bridgetown collection name and layout instead. See the [CHANGELOG](CHANGELOG.md) for details.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "date"
4
+ require "fileutils"
3
5
  require "yaml"
4
6
 
5
7
  module BridgetownDirectus
@@ -35,23 +37,84 @@ module BridgetownDirectus
35
37
  end
36
38
  end
37
39
 
38
- # Write a Directus item as a Markdown file in the correct Bridgetown collection directory
39
- def write_directus_file(item, collection_dir, layout = nil, api_url = nil)
40
- require "fileutils"
41
- FileUtils.mkdir_p(collection_dir)
42
- slug = item["slug"] || item["id"].to_s
43
- filename = build_filename(collection_dir, slug)
44
- item = transform_item_fields(item, api_url, layout)
45
- item["directus_generated"] = true # Add flag to front matter
46
- content = item.delete("body") || ""
47
- front_matter = generate_front_matter(item)
48
- write_markdown_file(filename, front_matter, content)
40
+ def build_directus_payload(item, collection_dir, collection_config, api_url = nil)
41
+ mapped_item = apply_data_mapping(item, collection_config)
42
+ slug = normalize_slug(mapped_item)
43
+ mapped_item["slug"] = slug
44
+ filename = build_filename(collection_dir, collection_config, mapped_item, slug)
45
+ mapped_item = transform_item_fields(mapped_item, api_url, collection_config.layout)
46
+ mapped_item["directus_generated"] = true # Add flag to front matter
47
+ content = mapped_item.delete("body") || ""
48
+ front_matter = generate_front_matter(mapped_item)
49
+ payload = render_markdown(front_matter, content)
50
+ [filename, payload]
49
51
  end
50
52
 
51
- def build_filename(collection_dir, slug)
53
+ def build_filename(collection_dir, collection_config, item, slug)
54
+ if collection_config.resource_type == :posts || collection_config.name.to_s == "posts"
55
+ post_date = extract_post_date(item)
56
+ if post_date
57
+ return File.join(collection_dir, "#{post_date.strftime("%Y-%m-%d")}-#{slug}.md")
58
+ end
59
+ end
60
+
52
61
  File.join(collection_dir, "#{slug}.md")
53
62
  end
54
63
 
64
+ def normalize_slug(item)
65
+ slug = item["slug"] || item[:slug]
66
+ slug = slug.to_s.strip
67
+ return slug unless slug.empty?
68
+
69
+ title = item["title"] || item[:title]
70
+ if title && defined?(Bridgetown::Utils) && Bridgetown::Utils.respond_to?(:slugify)
71
+ slug = Bridgetown::Utils.slugify(title.to_s)
72
+ else
73
+ slug = title.to_s.downcase.gsub(/[^a-z0-9]+/, "-").gsub(/^-|-$/, "")
74
+ end
75
+
76
+ slug = slug.strip
77
+ return slug unless slug.empty?
78
+
79
+ id = item["id"] || item[:id]
80
+ id.to_s
81
+ end
82
+
83
+ def apply_data_mapping(item, collection_config)
84
+ mapped_item = item.dup
85
+ return mapped_item unless collection_config.fields.any? || collection_config.translations_enabled
86
+
87
+ mapped_fields = if collection_config.translations_enabled
88
+ DataMapper.map_translations(collection_config, item, resolve_locale)
89
+ else
90
+ DataMapper.map(collection_config, item)
91
+ end
92
+
93
+ mapped_item.merge!(mapped_fields)
94
+ mapped_item
95
+ end
96
+
97
+ def resolve_locale
98
+ return site.locale if site.respond_to?(:locale) && site.locale
99
+
100
+ config_locale = site.config["locale"] || site.config[:locale]
101
+ return config_locale.to_sym if config_locale
102
+
103
+ :en
104
+ end
105
+
106
+ def extract_post_date(item)
107
+ raw = item["date"] || item[:date] || item["published_at"] || item[:published_at] || item["date_created"]
108
+ item["date"] ||= item["published_at"] if item["published_at"] && !item["date"]
109
+ return nil unless raw
110
+
111
+ return raw.to_date if raw.respond_to?(:to_date)
112
+
113
+ Date.parse(raw.to_s)
114
+ rescue ArgumentError
115
+ nil
116
+ end
117
+
55
118
  def transform_item_fields(item, api_url, layout)
56
119
  item = item.dup
57
120
  if item["image"] && api_url && !item["image"].to_s.start_with?("http://", "https://")
@@ -66,19 +129,41 @@ module BridgetownDirectus
66
129
  yaml.sub(%r{^---\s*\n}, "") # Remove leading --- if present
67
130
  end
68
131
 
69
- def write_markdown_file(filename, front_matter, content)
70
- File.write(filename, "---\n#{front_matter}---\n\n#{content}")
132
+ def render_markdown(front_matter, content)
133
+ "---\n#{front_matter}---\n\n#{content}"
134
+ end
135
+
136
+ def write_markdown_file(filename, payload)
137
+ FileUtils.mkdir_p(File.dirname(filename))
138
+ File.write(filename, payload)
139
+ end
140
+
141
+ def file_unchanged?(filename, payload)
142
+ return false unless File.exist?(filename)
143
+
144
+ File.read(filename) == payload
145
+ rescue StandardError
146
+ false
71
147
  end
72
148
 
73
149
  # Remove only plugin-generated Markdown files in the target directory before writing new ones
74
- def clean_collection_directory(collection_dir)
75
- require "yaml"
150
+ def clean_collection_directory(collection_dir, keep_files: [])
151
+ keep_set = keep_files.map { |file| File.expand_path(file) }.to_h { |file| [file, true] }
152
+
153
+ deleted = 0
76
154
  Dir.glob(File.join(collection_dir, "*.md")).each do |file|
155
+ next if keep_set[File.expand_path(file)]
156
+
77
157
  fm = File.read(file)[%r{\A---.*?---}m]
78
- File.delete(file) if fm && YAML.safe_load(fm)["directus_generated"]
158
+ if fm && YAML.safe_load(fm, permitted_classes: [Date, Time, DateTime])["directus_generated"]
159
+ File.delete(file)
160
+ deleted += 1
161
+ end
79
162
  rescue StandardError => e
80
163
  warn "[BridgetownDirectus] Could not check/delete #{file}: #{e.message}"
81
164
  end
165
+
166
+ deleted
82
167
  end
83
168
 
84
169
  def process_collection(client:, collection_config:)
@@ -90,12 +175,41 @@ module BridgetownDirectus
90
175
  return
91
176
  end
92
177
  collection_dir = collection_directory(collection_config.name)
93
- clean_collection_directory(collection_dir)
178
+ FileUtils.mkdir_p(collection_dir)
94
179
  api_url = site.config.bridgetown_directus.api_url
95
180
  sanitized_response = sanitize_keys(response)
96
- sanitized_response.each do |item|
97
- write_directus_file(item, collection_dir, collection_config.layout, api_url)
181
+ payloads = sanitized_response.to_h do |item|
182
+ build_directus_payload(item, collection_dir, collection_config, api_url)
98
183
  end
184
+ log_directus("Generating #{collection_label(collection_config)} (#{payloads.size} items)")
185
+ deleted = clean_collection_directory(collection_dir, keep_files: payloads.keys)
186
+ written = 0
187
+ skipped = 0
188
+ payloads.each do |filename, payload|
189
+ if file_unchanged?(filename, payload)
190
+ skipped += 1
191
+ next
192
+ end
193
+
194
+ write_markdown_file(filename, payload)
195
+ written += 1
196
+ end
197
+ log_directus("Updated #{collection_label(collection_config)}: wrote #{written}, skipped #{skipped}, deleted #{deleted}")
198
+ end
199
+
200
+ def log_directus(message)
201
+ return unless directus_logging_enabled?
202
+
203
+ Utils.log_directus(message)
204
+ end
205
+
206
+ def directus_logging_enabled?
207
+ flag = ENV["BRIDGETOWN_DIRECTUS_LOG"]
208
+ flag && !flag.to_s.strip.empty? && flag.to_s != "0"
209
+ end
210
+
211
+ def collection_label(collection_config)
212
+ collection_config.name.to_s.tr("_", " ").split.map(&:capitalize).join(" ")
99
213
  end
100
214
 
101
215
  # Recursively sanitize keys to avoid illegal instance variable names (Ruby 3.4+)
@@ -113,7 +113,15 @@ module BridgetownDirectus
113
113
  # Get the Directus field name for this Bridgetown field
114
114
  field_config = collection_config.fields[field]
115
115
 
116
- directus_field = field_config.is_a?(Hash) ? field_config[:directus_field] : field_config.to_s
116
+ directus_field = if field_config.nil?
117
+ field.to_s
118
+ elsif field_config.is_a?(Hash)
119
+ field_config[:directus_field]
120
+ else
121
+ field_config.to_s
122
+ end
123
+
124
+ directus_field = field.to_s if directus_field.to_s.empty?
117
125
 
118
126
  # Check if the translation has this field
119
127
  next unless translation[directus_field]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BridgetownDirectus
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -14,8 +14,8 @@ module BridgetownDirectus
14
14
  config.bridgetown_directus ||= Configuration.new
15
15
 
16
16
  # Set up configuration directly (leave to user initializer if possible)
17
- config.bridgetown_directus.api_url ||= ENV["DIRECTUS_API_URL"] || "[https://studio.munkun.com](https://studio.munkun.com)"
18
- config.bridgetown_directus.token ||= ENV["DIRECTUS_TOKEN"] || "t1P6YstcUslmf-KJFbc6Kyg0bomMxkXY"
17
+ config.bridgetown_directus.api_url ||= ENV["DIRECTUS_API_URL"]
18
+ config.bridgetown_directus.token ||= ENV["DIRECTUS_API_TOKEN"] || ENV["DIRECTUS_TOKEN"]
19
19
 
20
20
  # Register the builder
21
21
  config.builder BridgetownDirectus::Builder
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bridgetown_directus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Munkun
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-16 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bridgetown
@@ -160,7 +160,12 @@ executables: []
160
160
  extensions: []
161
161
  extra_rdoc_files: []
162
162
  files:
163
+ - ".github/workflows/ci.yml"
164
+ - ".github/workflows/release-please.yml"
165
+ - ".github/workflows/release.yml"
163
166
  - ".gitignore"
167
+ - ".release-please-config.json"
168
+ - ".release-please-manifest.json"
164
169
  - ".rubocop.yml"
165
170
  - CHANGELOG.md
166
171
  - Gemfile
@@ -201,7 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
206
  - !ruby/object:Gem::Version
202
207
  version: '0'
203
208
  requirements: []
204
- rubygems_version: 3.6.2
209
+ rubygems_version: 3.7.2
205
210
  specification_version: 4
206
211
  summary: Use Directus as headless CMS for Bridgetown
207
212
  test_files: []