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 +4 -4
- data/.github/workflows/ci.yml +23 -0
- data/.github/workflows/release-please.yml +20 -0
- data/.github/workflows/release.yml +28 -0
- data/.gitignore +3 -0
- data/.release-please-config.json +10 -0
- data/.release-please-manifest.json +3 -0
- data/CHANGELOG.md +7 -0
- data/README.md +18 -3
- data/lib/bridgetown_directus/builder.rb +134 -20
- data/lib/bridgetown_directus/data_mapper.rb +9 -1
- data/lib/bridgetown_directus/version.rb +1 -1
- data/lib/bridgetown_directus.rb +2 -2
- metadata +8 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6834fa39c0868873782728053787ca9fbcbd0defb231b70621c8737ba6e996ee
|
|
4
|
+
data.tar.gz: 9dfb896e01a1a7e48d0a6db38b41c12a092195650ea8945083b417d34a76bda6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
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
|
|
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
|
|
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
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
97
|
-
|
|
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.
|
|
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]
|
data/lib/bridgetown_directus.rb
CHANGED
|
@@ -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"]
|
|
18
|
-
config.bridgetown_directus.token ||= ENV["
|
|
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.
|
|
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:
|
|
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.
|
|
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: []
|