pandorga 1.2.4 → 1.3.1
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/CHANGELOG.md +17 -0
- data/DESIGN.md +6 -5
- data/_includes/content-runtime/43-blocks.html +50 -1
- data/_includes/content-runtime/90-bootstrap.html +1 -0
- data/_includes/home/band-listing.html +13 -1
- data/_layouts/home.html +31 -6
- data/_layouts/projects.html +3 -0
- data/assets/css/ledger/home.css +136 -27
- data/assets/css/ledger/projects.css +11 -0
- data/lib/pandorga/commands/new.rb +2 -2
- data/lib/pandorga/commands/publish.rb +2 -1
- data/lib/pandorga/version.rb +1 -1
- data/scripts/content/publish-content-to-object-store.sh +16 -1
- data/scripts/test/test-new.rb +2 -2
- data/scripts/test/test-publish-site-root.rb +40 -0
- data/scripts/validate.sh +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4947a2b9a5122c400a00753c4833861bbc6217dfaf59850fa27f79dbd7d99f3a
|
|
4
|
+
data.tar.gz: 7baa3fdc30880e278c772c2a98229bbeca3cce8b91e897bb56f4bb52fcedef3b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 060666c1506de360e6420c3b4cf77e5fe36a8180f1aa5e8ffc5f8e4c9de134d980f1c2ba336a799cd4ac19646dc3ec6bb4022c78f0291d9eca2640bdf63da17d
|
|
7
|
+
data.tar.gz: 2a305ce4521e205322f8d7329d25730b4593e42718c8becfb49eed02bf4c3e1b862cec55b55662d7d653ea47d668cf8d3b780a4bd17f4b4bfb4b43bc7f20974d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.3.1] — 2026-10-05
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Home portfolio band is a specimen + inventory bench (16:9 media) instead of
|
|
8
|
+
an equal card grid
|
|
9
|
+
- Projects listing lead shows a Featured chip
|
|
10
|
+
|
|
11
|
+
## [1.3.0] — 2026-10-05
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Object-store publish fails fast when `PANDORGA_SITE_ROOT` is unset and the
|
|
16
|
+
script runs from the pandorga gem (no more silent skip of `content/media`)
|
|
17
|
+
- `pandorga publish` exports `PANDORGA_SITE_ROOT` even when delegating to a
|
|
18
|
+
site-local `publish-content-to-r2.sh` wrapper
|
|
19
|
+
|
|
3
20
|
## [1.2.4] — 2026-10-05
|
|
4
21
|
|
|
5
22
|
### Fixed
|
data/DESIGN.md
CHANGED
|
@@ -935,8 +935,9 @@ Letter-spacing in use, by frequency: `0.1em` (12), `0.08em` (10), `0.12em` (7),
|
|
|
935
935
|
writing, rascunhos, systems, ideas graph. The featured band carries a 6rem ×
|
|
936
936
|
3px `primary` rule at its top-left corner as a structural mark. The three
|
|
937
937
|
writing cards take `primary` / `secondary` / `tertiary` 2px top rules in
|
|
938
|
-
rotation. The systems
|
|
939
|
-
|
|
938
|
+
rotation. The systems band is a specimen + inventory bench from 900px —
|
|
939
|
+
one large project plate (~7/12) beside a compact ledger rail (~5/12); both
|
|
940
|
+
keep 16:9 media. Below 768px the hero portrait runs to the screen edge, pulled
|
|
940
941
|
out of the band's gutter by exactly `--gutter-mobile` on each side and losing
|
|
941
942
|
its side hairlines, which at the edge would read as a frame cut off.
|
|
942
943
|
* **Writing index.** Masthead over a hairline, then a `panelContrast` filter
|
|
@@ -947,9 +948,9 @@ Letter-spacing in use, by frequency: `0.1em` (12), `0.08em` (10), `0.12em` (7),
|
|
|
947
948
|
rows beside a sticky marginalia column. Rows become two-column
|
|
948
949
|
(`.rascunhos-row--illustrated`, a 7.5rem plate, 4rem on phones) only when an
|
|
949
950
|
image is actually present.
|
|
950
|
-
* **Projects.** A
|
|
951
|
-
`align-items: start`, so a
|
|
952
|
-
one that has one.
|
|
951
|
+
* **Projects.** A session-featured lead plate (with a Featured chip) over a
|
|
952
|
+
`grid-cols-1 md:grid-cols-2` of system cards with `align-items: start`, so a
|
|
953
|
+
card without a poster does not stretch to match one that has one.
|
|
953
954
|
* **Sources.** `.listing-card-grid--sources`: one column, two from 768px,
|
|
954
955
|
three from 1024px, posters at `16 / 9` — the video frame, which is what most
|
|
955
956
|
of the collection links to. Filtering hides pre-rendered cards with a class
|
|
@@ -324,6 +324,43 @@
|
|
|
324
324
|
].join('');
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
/* Compact inventory row for the home portfolio bench — thumb + name +
|
|
328
|
+
* status only. Field-backed; no tagline or tag cloud. */
|
|
329
|
+
function renderProjectInventoryRow(item, options) {
|
|
330
|
+
options = options || {};
|
|
331
|
+
var f = blockFields(item);
|
|
332
|
+
var fm = f.fm;
|
|
333
|
+
var href = options.href || '#';
|
|
334
|
+
var name = fm.project || f.title;
|
|
335
|
+
|
|
336
|
+
var row = document.createElement('article');
|
|
337
|
+
row.className = 'home-project-inventory-row group';
|
|
338
|
+
|
|
339
|
+
var media = renderPosterMedia(fm.thumbnail, name);
|
|
340
|
+
if (!media && fm.icon) {
|
|
341
|
+
media = '<div class="home-project-inventory-mark" aria-hidden="true">' +
|
|
342
|
+
'<span class="material-symbols-outlined">' + escapeHtml(fm.icon) + '</span></div>';
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
var html = '';
|
|
346
|
+
if (media) {
|
|
347
|
+
html += '<div class="home-project-inventory-media">' + media + '</div>';
|
|
348
|
+
}
|
|
349
|
+
html += '<div class="home-project-inventory-body">';
|
|
350
|
+
html += '<h3 class="home-project-inventory-title card-title">' +
|
|
351
|
+
'<a class="ledger-card-stretch" href="' + escapeAttr(href) + '">' +
|
|
352
|
+
escapeHtml(name) + '</a></h3>';
|
|
353
|
+
var meta = renderMetaStrip([
|
|
354
|
+
fm.label ? escapeHtml(fm.label) : '',
|
|
355
|
+
fm.status ? '<span class="ledger-status">' + escapeHtml(fm.status) + '</span>' : ''
|
|
356
|
+
]);
|
|
357
|
+
if (meta) html += meta;
|
|
358
|
+
html += '</div>';
|
|
359
|
+
|
|
360
|
+
row.innerHTML = html;
|
|
361
|
+
return row;
|
|
362
|
+
}
|
|
363
|
+
|
|
327
364
|
/* System card — the project shape. Carries the fields projects actually
|
|
328
365
|
* have: label, status, and a repository link. */
|
|
329
366
|
function renderSystemCard(item, options) {
|
|
@@ -335,8 +372,20 @@
|
|
|
335
372
|
|
|
336
373
|
var card = document.createElement('article');
|
|
337
374
|
card.className = 'ledger-system-card group';
|
|
375
|
+
if (options.className) card.className += ' ' + options.className;
|
|
338
376
|
|
|
339
|
-
var html =
|
|
377
|
+
var html = '';
|
|
378
|
+
if (options.frameCorners) {
|
|
379
|
+
html += '<span class="ledger-frame-corners" aria-hidden="true"></span>';
|
|
380
|
+
}
|
|
381
|
+
var poster = renderPosterMedia(fm.thumbnail, name);
|
|
382
|
+
if (poster) {
|
|
383
|
+
html += poster;
|
|
384
|
+
} else if (options.glyphFallback && fm.icon) {
|
|
385
|
+
html += '<div class="projects-plate projects-plate--mark" aria-hidden="true">' +
|
|
386
|
+
'<span class="material-symbols-outlined projects-plate-glyph">' +
|
|
387
|
+
escapeHtml(fm.icon) + '</span></div>';
|
|
388
|
+
}
|
|
340
389
|
// Callers may supply their own strip parts; the default is whatever the
|
|
341
390
|
// project declares about itself.
|
|
342
391
|
var parts = options.metaParts || [
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
renderLedgerRow: renderLedgerRow,
|
|
64
64
|
renderArchiveCard: renderArchiveCard,
|
|
65
65
|
renderSystemCard: renderSystemCard,
|
|
66
|
+
renderProjectInventoryRow: renderProjectInventoryRow,
|
|
66
67
|
renderPosterMedia: renderPosterMedia,
|
|
67
68
|
ensureBibliographyIndex: ensureBibliographyIndex,
|
|
68
69
|
bibField: bibField,
|
|
@@ -39,11 +39,23 @@
|
|
|
39
39
|
{%- if _featured -%}
|
|
40
40
|
<div id="{{ _slot_id }}-featured" data-home-featured-slot data-tag-base="{{ _path }}"></div>
|
|
41
41
|
{%- endif -%}
|
|
42
|
+
{%- if band.kind == 'portfolio' -%}
|
|
42
43
|
<div
|
|
43
44
|
id="{{ _slot_id }}"
|
|
44
|
-
class="
|
|
45
|
+
class="home-projects-bench"
|
|
46
|
+
data-home-list
|
|
47
|
+
data-tag-base="{{ _path }}"
|
|
48
|
+
>
|
|
49
|
+
<div data-home-specimen></div>
|
|
50
|
+
<div class="home-project-inventory" data-home-inventory aria-label="Project inventory"></div>
|
|
51
|
+
</div>
|
|
52
|
+
{%- else -%}
|
|
53
|
+
<div
|
|
54
|
+
id="{{ _slot_id }}"
|
|
55
|
+
class="{% if band.kind == 'blog' %}home-draft-list{% else %}home-card-grid{% endif %}"
|
|
45
56
|
data-home-list
|
|
46
57
|
data-tag-base="{{ _path }}"
|
|
47
58
|
></div>
|
|
59
|
+
{%- endif -%}
|
|
48
60
|
</div>
|
|
49
61
|
</section>
|
data/_layouts/home.html
CHANGED
|
@@ -208,13 +208,38 @@ layout: shell
|
|
|
208
208
|
entries = entries.sort(function (a, b) {
|
|
209
209
|
return Number(a.sort_order || 0) - Number(b.sort_order || 0);
|
|
210
210
|
});
|
|
211
|
+
var specimenSlot = section.querySelector('[data-home-specimen]');
|
|
212
|
+
var inventorySlot = section.querySelector('[data-home-inventory]');
|
|
211
213
|
var systemTagHref = tagHrefFrom(list);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
214
|
+
var portfolio = entries.slice(0, limit);
|
|
215
|
+
if (!portfolio.length) {
|
|
216
|
+
runtime.Listing.showErrorState(list, 'Projects could not be loaded right now.');
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (specimenSlot) {
|
|
220
|
+
specimenSlot.innerHTML = '';
|
|
221
|
+
specimenSlot.appendChild(runtime.renderSystemCard(asItem(portfolio[0]), {
|
|
222
|
+
href: portfolio[0].url || runtime.projectUrl(portfolio[0].slug),
|
|
223
|
+
tagHref: systemTagHref,
|
|
224
|
+
className: 'home-project-specimen ledger-card--edge-primary',
|
|
225
|
+
frameCorners: true,
|
|
226
|
+
glyphFallback: true
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
var rest = portfolio.slice(1);
|
|
230
|
+
if (inventorySlot) {
|
|
231
|
+
if (!rest.length) {
|
|
232
|
+
inventorySlot.hidden = true;
|
|
233
|
+
inventorySlot.innerHTML = '';
|
|
234
|
+
} else {
|
|
235
|
+
inventorySlot.hidden = false;
|
|
236
|
+
fill(inventorySlot, rest, function (entry) {
|
|
237
|
+
return runtime.renderProjectInventoryRow(asItem(entry), {
|
|
238
|
+
href: entry.url || runtime.projectUrl(entry.slug)
|
|
239
|
+
});
|
|
240
|
+
}, 'Projects could not be loaded right now.');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
218
243
|
return;
|
|
219
244
|
}
|
|
220
245
|
|
data/_layouts/projects.html
CHANGED
|
@@ -235,7 +235,10 @@ layout: shell
|
|
|
235
235
|
html += '<a class="projects-lead-media" href="' + escAttr(href) + '" tabindex="-1">' +
|
|
236
236
|
plateHtml(item, 'projects-plate--lead') + '</a>';
|
|
237
237
|
html += '<div class="projects-lead-body">';
|
|
238
|
+
html += '<div class="projects-lead-rail">';
|
|
239
|
+
html += '<span class="ledger-chip home-featured-badge">Featured</span>';
|
|
238
240
|
if (line) html += line;
|
|
241
|
+
html += '</div>';
|
|
239
242
|
html += '<h2 class="projects-lead-title"><a href="' + escAttr(href) + '">' + esc(name) + '</a></h2>';
|
|
240
243
|
if (fm.title && fm.title !== name) {
|
|
241
244
|
html += '<p class="projects-lead-subtitle">' + esc(fm.title) + '</p>';
|
data/assets/css/ledger/home.css
CHANGED
|
@@ -710,8 +710,8 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
710
710
|
.home-card-grid .ledger-card-title,
|
|
711
711
|
.home-card-grid .card-title,
|
|
712
712
|
.home-draft-card .ledger-card-title,
|
|
713
|
-
.home-
|
|
714
|
-
.home-
|
|
713
|
+
.home-project-specimen .ledger-card-title,
|
|
714
|
+
.home-project-inventory-title {
|
|
715
715
|
font-family: var(--font-content-title);
|
|
716
716
|
font-weight: var(--weight-title);
|
|
717
717
|
font-synthesis: none;
|
|
@@ -943,45 +943,57 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
943
943
|
}
|
|
944
944
|
}
|
|
945
945
|
|
|
946
|
-
/* ── Engineering artifacts ──
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
.home-system-grid {
|
|
946
|
+
/* ── Engineering artifacts: specimen + inventory bench ──
|
|
947
|
+
One large specimen plate (~7/12) beside a compact ledger rail (~5/12).
|
|
948
|
+
Covers stay 16:9 — hard requirement for home project media. */
|
|
949
|
+
.home-projects-bench {
|
|
951
950
|
display: grid;
|
|
952
951
|
grid-template-columns: minmax(0, 1fr);
|
|
953
|
-
gap: var(--space-
|
|
954
|
-
align-items:
|
|
952
|
+
gap: var(--space-lg);
|
|
953
|
+
align-items: start;
|
|
955
954
|
}
|
|
956
955
|
|
|
957
|
-
@media (min-width:
|
|
958
|
-
.home-
|
|
959
|
-
grid-template-columns:
|
|
956
|
+
@media (min-width: 900px) {
|
|
957
|
+
.home-projects-bench {
|
|
958
|
+
grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
|
|
959
|
+
gap: var(--space-xl);
|
|
960
960
|
}
|
|
961
961
|
}
|
|
962
962
|
|
|
963
|
-
.home-
|
|
964
|
-
|
|
965
|
-
|
|
963
|
+
.home-project-specimen {
|
|
964
|
+
position: relative;
|
|
965
|
+
gap: var(--space-sm);
|
|
966
|
+
padding: var(--space-lg);
|
|
966
967
|
}
|
|
967
968
|
|
|
968
|
-
.home-
|
|
969
|
-
.home-
|
|
969
|
+
.home-project-specimen>.ledger-poster,
|
|
970
|
+
.home-project-specimen>.projects-plate {
|
|
970
971
|
width: auto;
|
|
971
|
-
margin: calc(var(--space-
|
|
972
|
-
aspect-ratio:
|
|
972
|
+
margin: calc(var(--space-lg) * -1) calc(var(--space-lg) * -1) 0;
|
|
973
|
+
aspect-ratio: 16 / 9;
|
|
973
974
|
}
|
|
974
975
|
|
|
975
|
-
.home-
|
|
976
|
-
|
|
977
|
-
|
|
976
|
+
.home-project-specimen .projects-plate--mark {
|
|
977
|
+
display: flex;
|
|
978
|
+
align-items: center;
|
|
979
|
+
justify-content: center;
|
|
980
|
+
background: var(--color-panel-contrast);
|
|
981
|
+
color: var(--color-primary);
|
|
978
982
|
}
|
|
979
983
|
|
|
980
|
-
.home-
|
|
981
|
-
|
|
984
|
+
.home-project-specimen .projects-plate-glyph {
|
|
985
|
+
font-size: 3rem;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
.home-project-specimen .ledger-card-title {
|
|
989
|
+
font-family: var(--font-content-title);
|
|
990
|
+
font-size: var(--type-card-lg);
|
|
991
|
+
font-weight: var(--weight-title);
|
|
992
|
+
font-synthesis: none;
|
|
993
|
+
line-height: 1.2;
|
|
982
994
|
}
|
|
983
995
|
|
|
984
|
-
.home-
|
|
996
|
+
.home-project-specimen .ledger-card-copy {
|
|
985
997
|
font-family: var(--font-body);
|
|
986
998
|
font-size: var(--type-subtitle);
|
|
987
999
|
font-weight: var(--weight-body);
|
|
@@ -989,9 +1001,106 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
989
1001
|
color: var(--color-legend);
|
|
990
1002
|
display: -webkit-box;
|
|
991
1003
|
-webkit-box-orient: vertical;
|
|
992
|
-
-webkit-line-clamp:
|
|
993
|
-
line-clamp:
|
|
1004
|
+
-webkit-line-clamp: 3;
|
|
1005
|
+
line-clamp: 3;
|
|
1006
|
+
overflow: hidden;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
.home-project-specimen .ledger-card-links {
|
|
1010
|
+
row-gap: var(--space-sm);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.home-project-inventory {
|
|
1014
|
+
display: flex;
|
|
1015
|
+
flex-direction: column;
|
|
1016
|
+
min-width: 0;
|
|
1017
|
+
border-top: 1px solid var(--border-gentle-divider);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
.home-project-inventory-row {
|
|
1021
|
+
position: relative;
|
|
1022
|
+
display: grid;
|
|
1023
|
+
grid-template-columns: minmax(0, 1fr);
|
|
1024
|
+
gap: var(--space-md);
|
|
1025
|
+
align-items: center;
|
|
1026
|
+
padding: var(--space-sm) 0;
|
|
1027
|
+
border-bottom: 1px solid var(--border-gentle-divider);
|
|
1028
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
.home-project-inventory-row:has(.home-project-inventory-media) {
|
|
1032
|
+
grid-template-columns: 5.5rem minmax(0, 1fr);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
.home-project-inventory-row:hover,
|
|
1036
|
+
.home-project-inventory-row:focus-within {
|
|
1037
|
+
background: var(--color-surface-high);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
.home-band--contrast .home-project-inventory-row:hover,
|
|
1041
|
+
.home-band--contrast .home-project-inventory-row:focus-within {
|
|
1042
|
+
background: var(--color-surface-high-contrast);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.home-project-inventory-media {
|
|
1046
|
+
min-width: 0;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
.home-project-inventory-media .ledger-poster,
|
|
1050
|
+
.home-project-inventory-mark {
|
|
1051
|
+
aspect-ratio: 16 / 9;
|
|
1052
|
+
width: 100%;
|
|
994
1053
|
overflow: hidden;
|
|
1054
|
+
border: 1px solid var(--border-gentle-subtle);
|
|
1055
|
+
background: var(--color-panel-contrast);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
.home-project-inventory-media .ledger-poster img {
|
|
1059
|
+
width: 100%;
|
|
1060
|
+
height: 100%;
|
|
1061
|
+
object-fit: cover;
|
|
1062
|
+
display: block;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
.home-project-inventory-mark {
|
|
1066
|
+
display: flex;
|
|
1067
|
+
align-items: center;
|
|
1068
|
+
justify-content: center;
|
|
1069
|
+
color: var(--color-primary);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
.home-project-inventory-mark .material-symbols-outlined {
|
|
1073
|
+
font-size: 1.25rem;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
.home-project-inventory-body {
|
|
1077
|
+
display: flex;
|
|
1078
|
+
flex-direction: column;
|
|
1079
|
+
gap: var(--space-2xs);
|
|
1080
|
+
min-width: 0;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
.home-project-inventory-title {
|
|
1084
|
+
margin: 0;
|
|
1085
|
+
font-family: var(--font-content-title);
|
|
1086
|
+
font-size: var(--type-card);
|
|
1087
|
+
font-weight: var(--weight-title);
|
|
1088
|
+
font-synthesis: none;
|
|
1089
|
+
line-height: 1.25;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
.home-project-inventory-title a {
|
|
1093
|
+
color: var(--color-on-surface);
|
|
1094
|
+
text-decoration: none;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
.home-project-inventory-row:hover .home-project-inventory-title a,
|
|
1098
|
+
.home-project-inventory-row:focus-within .home-project-inventory-title a {
|
|
1099
|
+
color: var(--color-primary);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
.home-project-inventory-row .ledger-meta {
|
|
1103
|
+
margin: 0;
|
|
995
1104
|
}
|
|
996
1105
|
|
|
997
1106
|
|
|
@@ -206,6 +206,17 @@
|
|
|
206
206
|
padding: var(--space-lg);
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
.projects-page .projects-lead-rail {
|
|
210
|
+
display: flex;
|
|
211
|
+
flex-wrap: wrap;
|
|
212
|
+
align-items: center;
|
|
213
|
+
gap: var(--space-sm) var(--space-md);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.projects-page .projects-lead-rail .ledger-meta {
|
|
217
|
+
margin: 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
209
220
|
.projects-page .projects-lead-title {
|
|
210
221
|
font-family: var(--font-content-title);
|
|
211
222
|
font-size: var(--type-card-lg);
|
|
@@ -22,9 +22,9 @@ module Pandorga
|
|
|
22
22
|
|
|
23
23
|
source "https://rubygems.org"
|
|
24
24
|
|
|
25
|
-
gem "pandorga", "~> 1.
|
|
25
|
+
gem "pandorga", "~> 1.3"
|
|
26
26
|
# Local checkout: gem "pandorga", path: "../pandorga"
|
|
27
|
-
# GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v1.
|
|
27
|
+
# GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v1.3.1"
|
|
28
28
|
gem "csv"
|
|
29
29
|
gem "base64"
|
|
30
30
|
RUBY
|
|
@@ -12,8 +12,9 @@ module Pandorga
|
|
|
12
12
|
root = Pathname.new(argv[0] || Dir.pwd).expand_path
|
|
13
13
|
site_script = root.join("scripts", "content", "publish-content-to-r2.sh")
|
|
14
14
|
if site_script.file? && site_script.expand_path != gem_publish_script
|
|
15
|
+
env = ENV.to_h.merge("PANDORGA_SITE_ROOT" => root.to_s)
|
|
15
16
|
Dir.chdir(root) do
|
|
16
|
-
exec("bash", site_script.to_s, *argv[1..])
|
|
17
|
+
exec(env, "bash", site_script.to_s, *argv[1..])
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
|
data/lib/pandorga/version.rb
CHANGED
|
@@ -5,7 +5,19 @@
|
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
|
|
7
7
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
8
|
+
|
|
9
|
+
# When this script lives in an installed (or checked-out) gem, ROOT is the gem
|
|
10
|
+
# tree — not the consumer site. Media sync must use the site root; without it
|
|
11
|
+
# we used to skip content/media silently and leave Studio uploads on GitHub
|
|
12
|
+
# only. Require an explicit site root whenever ROOT looks like the gem.
|
|
13
|
+
if [ -z "${PANDORGA_SITE_ROOT:-}" ] && [ -f "$ROOT/pandorga.gemspec" ]; then
|
|
14
|
+
echo "error: PANDORGA_SITE_ROOT must be set when publish-content-to-object-store.sh runs from the pandorga gem." >&2
|
|
15
|
+
echo "hint: export PANDORGA_SITE_ROOT to the Jekyll site root before exec (site wrappers must set this)." >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
8
19
|
SITE_ROOT="${PANDORGA_SITE_ROOT:-$ROOT}"
|
|
20
|
+
SITE_ROOT="$(cd "$SITE_ROOT" && pwd)"
|
|
9
21
|
EXPORT_ROOT="${1:-$SITE_ROOT/_content_json}"
|
|
10
22
|
|
|
11
23
|
# Prefer S3_* names; fall back to R2_* for Cloudflare R2 callers.
|
|
@@ -55,6 +67,7 @@ sync_json_prefix() {
|
|
|
55
67
|
|
|
56
68
|
echo "==> Publishing content JSON to bucket: ${S3_BUCKET}"
|
|
57
69
|
echo "==> Endpoint: ${S3_ENDPOINT}"
|
|
70
|
+
echo "==> Site root: ${SITE_ROOT}"
|
|
58
71
|
sync_json_prefix collections
|
|
59
72
|
sync_json_prefix data
|
|
60
73
|
sync_json_prefix pages
|
|
@@ -67,12 +80,14 @@ aws s3 cp "$EXPORT_ROOT/manifest.json" "s3://${S3_BUCKET}/manifest.json" \
|
|
|
67
80
|
|
|
68
81
|
MEDIA_ROOT="$SITE_ROOT/content/media"
|
|
69
82
|
if [ -d "$MEDIA_ROOT" ]; then
|
|
70
|
-
echo "==> Sync media/"
|
|
83
|
+
echo "==> Sync media/ from ${MEDIA_ROOT}"
|
|
71
84
|
aws s3 sync "$MEDIA_ROOT" "s3://${S3_BUCKET}/media" \
|
|
72
85
|
--endpoint-url "$S3_ENDPOINT" \
|
|
73
86
|
--delete \
|
|
74
87
|
--size-only \
|
|
75
88
|
--cache-control "public,max-age=31536000,immutable"
|
|
89
|
+
else
|
|
90
|
+
echo "==> No content/media at ${MEDIA_ROOT} (skipping media sync)"
|
|
76
91
|
fi
|
|
77
92
|
|
|
78
93
|
echo "==> Object-store publish complete"
|
data/scripts/test/test-new.rb
CHANGED
|
@@ -57,8 +57,8 @@ end
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
gemfile = DEST.join("Gemfile").read
|
|
60
|
-
unless gemfile.include?('gem "pandorga", "~> 1.
|
|
61
|
-
warn "FAIL test-new: Gemfile must pin pandorga ~> 1.
|
|
60
|
+
unless gemfile.include?('gem "pandorga", "~> 1.3"')
|
|
61
|
+
warn "FAIL test-new: Gemfile must pin pandorga ~> 1.3\n#{gemfile}"
|
|
62
62
|
exit 1
|
|
63
63
|
end
|
|
64
64
|
unless gemfile.include?("path:") && gemfile.include?("github:")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Refs: media sync must not silently use the gem tree as SITE_ROOT.
|
|
5
|
+
require "open3"
|
|
6
|
+
require "pathname"
|
|
7
|
+
|
|
8
|
+
ROOT = Pathname.new(__dir__).join("../..").expand_path
|
|
9
|
+
SCRIPT = ROOT.join("scripts/content/publish-content-to-object-store.sh")
|
|
10
|
+
|
|
11
|
+
def fail!(message)
|
|
12
|
+
warn "FAIL test-publish-site-root: #{message}"
|
|
13
|
+
exit 1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
fail!("missing #{SCRIPT}") unless SCRIPT.file?
|
|
17
|
+
|
|
18
|
+
env = ENV.to_h
|
|
19
|
+
env.delete("PANDORGA_SITE_ROOT")
|
|
20
|
+
# Avoid falling through to real credentials if a developer has them set.
|
|
21
|
+
%w[
|
|
22
|
+
S3_ENDPOINT S3_BUCKET S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY
|
|
23
|
+
R2_ENDPOINT R2_BUCKET R2_ACCESS_KEY_ID R2_SECRET_ACCESS_KEY
|
|
24
|
+
].each { |key| env.delete(key) }
|
|
25
|
+
|
|
26
|
+
out, status = Open3.capture2e(env, "bash", SCRIPT.to_s)
|
|
27
|
+
fail!("expected exit 1 without PANDORGA_SITE_ROOT, got #{status.exitstatus}") unless status.exitstatus == 1
|
|
28
|
+
fail!("expected PANDORGA_SITE_ROOT error, got:\n#{out}") unless out.include?("PANDORGA_SITE_ROOT must be set")
|
|
29
|
+
|
|
30
|
+
ok_env = env.merge(
|
|
31
|
+
"PANDORGA_SITE_ROOT" => ROOT.join("examples/minimal").to_s,
|
|
32
|
+
"S3_ENDPOINT" => "https://example.invalid",
|
|
33
|
+
"S3_BUCKET" => "test-bucket",
|
|
34
|
+
"S3_ACCESS_KEY_ID" => "test",
|
|
35
|
+
"S3_SECRET_ACCESS_KEY" => "test"
|
|
36
|
+
)
|
|
37
|
+
out2, status2 = Open3.capture2e(ok_env, "bash", SCRIPT.to_s, ROOT.join("examples/minimal/_content_json").to_s)
|
|
38
|
+
fail!("with PANDORGA_SITE_ROOT set, must not fail the site-root guard; got #{status2.exitstatus}:\n#{out2}") if out2.include?("PANDORGA_SITE_ROOT must be set")
|
|
39
|
+
|
|
40
|
+
puts "PASS test-publish-site-root"
|
data/scripts/validate.sh
CHANGED
|
@@ -34,6 +34,7 @@ TESTS=(
|
|
|
34
34
|
scripts/test/test-dual-template-instances.rb
|
|
35
35
|
scripts/test/test-dual-template-runtime.rb
|
|
36
36
|
scripts/test/test-install-functions.rb
|
|
37
|
+
scripts/test/test-publish-site-root.rb
|
|
37
38
|
scripts/test/test-no-personal-data.rb
|
|
38
39
|
scripts/test/test-configuration-doc.rb
|
|
39
40
|
scripts/test/test-cv-export-contract.rb
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pandorga
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The Pandorga contributors
|
|
@@ -395,6 +395,7 @@ files:
|
|
|
395
395
|
- scripts/test/test-new.rb
|
|
396
396
|
- scripts/test/test-no-personal-data.rb
|
|
397
397
|
- scripts/test/test-public-cv-export.rb
|
|
398
|
+
- scripts/test/test-publish-site-root.rb
|
|
398
399
|
- scripts/test/test-r2-config-consistency.rb
|
|
399
400
|
- scripts/test/test-r2-json-sync.rb
|
|
400
401
|
- scripts/test/test-registry-home.rb
|