ultimate-jekyll-manager 1.6.2 → 1.6.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.
package/CHANGELOG.md CHANGED
@@ -14,6 +14,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
14
14
  - `Fixed` for any bug fixes.
15
15
  - `Security` in case of vulnerabilities.
16
16
 
17
+ ---
18
+ ## [1.6.3] - 2026-06-03
19
+
20
+ ### Changed
21
+
22
+ - **Workflow template dynamically generates secrets from `.env`.** `defaults.js` reads the default `_.env`, extracts all key names, and produces a `{ github.secrets }` template variable — no more hardcoding individual secrets in `build.yml`.
23
+ - **`publishSecrets()` replaces `publishGitHubToken()` in setup.** Now reads the consumer's `.env` and publishes ALL non-empty keys as GitHub Actions repo secrets (not just `GH_TOKEN`).
24
+
25
+ ### Added
26
+
27
+ - **Country flag SVGs:** id, in, ph, pk, ru, vn (modern-square style).
28
+ - **Auto-create `pages/` dir for custom themes** in webpack.js — prevents `Module not found: __theme__/pages` error when a consumer theme lacks a `pages/` directory.
29
+
30
+ ### Removed
31
+
32
+ - **`BACKEND_MANAGER_KEY`** removed from workflow template (replaced by dynamic `.env` secrets).
33
+
17
34
  ---
18
35
  ## [1.6.2] - 2026-06-02
19
36
 
package/CLAUDE.md CHANGED
@@ -200,7 +200,7 @@ Deep references live in `docs/`. Treat docs as a first-class deliverable. **When
200
200
  - [docs/themes.md](docs/themes.md) — theme system: selection + resolution (SCSS loadPaths, `__theme__`, classy layout fallback), shared vs per-theme layers, authoring a theme inside UJM OR in a consumer project, live validation
201
201
  - [docs/layouts-and-pages.md](docs/layouts-and-pages.md) — page types, layout chain, `asset_path` frontmatter
202
202
  - [docs/images.md](docs/images.md) — `@post/` shortcut for blog post images, BEM admin/post image handling, imagemin pipeline + source-size constraints + `UJ_IMAGEMIN_REWRITE_SOURCES` cleanup flag
203
- - [docs/icons.md](docs/icons.md) — Font Awesome conventions, `{% uj_icon %}` vs prerendered icons in JS, size reference
203
+ - [docs/icons.md](docs/icons.md) — Font Awesome conventions, `{% uj_icon %}` vs prerendered icons in JS, size reference, country flag SVGs (`assets/icons/flags/modern-square/`)
204
204
  - [docs/seo.md](docs/seo.md) — Alternatives collection (competitor comparison pages) + Schema/JSON-LD (`SoftwareApplication`, `FAQPage`)
205
205
 
206
206
  ### Frontend behavior
@@ -0,0 +1 @@
1
+ <svg height="512" viewBox="0 0 152 152" width="512" xmlns="http://www.w3.org/2000/svg"><defs><clipPath id="clip"><path d="m124.81 149.4a459 459 0 0 1 -97.62 0 27.69 27.69 0 0 1 -24.59-24.59 459 459 0 0 1 0-97.62 27.69 27.69 0 0 1 24.59-24.59 459 459 0 0 1 97.62 0 27.69 27.69 0 0 1 24.59 24.59 459 459 0 0 1 0 97.62 27.69 27.69 0 0 1 -24.59 24.59z"/></clipPath></defs><g clip-path="url(#clip)"><rect width="152" height="76" fill="#f40055"/><rect y="76" width="152" height="76" fill="#f0f9ff"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg height="512" viewBox="0 0 152 152" width="512" xmlns="http://www.w3.org/2000/svg"><g id="Layer_2" data-name="Layer 2"><g id="india" data-name="india"><path id="path" d="m124.81 149.4a459 459 0 0 1 -97.62 0 27.69 27.69 0 0 1 -24.59-24.59 459 459 0 0 1 0-97.62 27.69 27.69 0 0 1 24.59-24.59 459 459 0 0 1 97.62 0 27.69 27.69 0 0 1 24.59 24.59 459 459 0 0 1 0 97.62 27.69 27.69 0 0 1 -24.59 24.59z" fill="#f0f9ff"/><path d="m151.38 52.26h-150.76q.63-12.54 2-25.08a27.68 27.68 0 0 1 24.57-24.58 459 459 0 0 1 97.62 0 27.68 27.68 0 0 1 24.59 24.58q1.33 12.53 1.98 25.08z" fill="#ff9933"/><path d="m151.38 99.73q-.63 12.54-2 25.08a27.68 27.68 0 0 1 -24.59 24.58 459 459 0 0 1 -97.62 0 27.68 27.68 0 0 1 -24.57-24.58q-1.34-12.54-2-25.08z" fill="#138808"/><circle cx="76" cy="76" r="13" fill="#000080"/><circle cx="76" cy="76" r="10.5" fill="#f0f9ff"/><circle cx="76" cy="76" r="3" fill="#000080"/></g></g></svg>
@@ -0,0 +1 @@
1
+ <svg height="512" viewBox="0 0 152 152" width="512" xmlns="http://www.w3.org/2000/svg"><defs><clipPath id="clip"><path d="m124.81 149.4a459 459 0 0 1 -97.62 0 27.69 27.69 0 0 1 -24.59-24.59 459 459 0 0 1 0-97.62 27.69 27.69 0 0 1 24.59-24.59 459 459 0 0 1 97.62 0 27.69 27.69 0 0 1 24.59 24.59 459 459 0 0 1 0 97.62 27.69 27.69 0 0 1 -24.59 24.59z"/></clipPath></defs><g clip-path="url(#clip)"><rect width="152" height="76" fill="#406bd4"/><rect y="76" width="152" height="76" fill="#f40055"/><path d="m0 0v152l76-76z" fill="#f0f9ff"/><g fill="#ffcb24"><circle cx="25" cy="76" r="7"/><circle cx="10" cy="24" r="3.5"/><circle cx="10" cy="128" r="3.5"/><circle cx="52" cy="76" r="3.5"/></g></g></svg>
@@ -0,0 +1 @@
1
+ <svg height="512" viewBox="0 0 152 152" width="512" xmlns="http://www.w3.org/2000/svg"><defs><clipPath id="clip"><path d="m124.81 149.4a459 459 0 0 1 -97.62 0 27.69 27.69 0 0 1 -24.59-24.59 459 459 0 0 1 0-97.62 27.69 27.69 0 0 1 24.59-24.59 459 459 0 0 1 97.62 0 27.69 27.69 0 0 1 24.59 24.59 459 459 0 0 1 0 97.62 27.69 27.69 0 0 1 -24.59 24.59z"/></clipPath></defs><g clip-path="url(#clip)"><rect width="152" height="152" fill="#01411c"/><rect width="38" height="152" fill="#f0f9ff"/><g transform="rotate(-270, 90, 76)"><circle cx="90" cy="76" r="24" fill="#f0f9ff"/><circle cx="82" cy="68" r="20" fill="#01411c"/></g><path d="m117.72 58.47a5 5 0 0 0 -4.5-5 5 5 0 0 0 -10 0 5 5 0 0 0 0 10 5 5 0 0 0 10 0 5 5 0 0 0 4.5-5z" fill="#f0f9ff"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg height="512" viewBox="0 0 152 152" width="512" xmlns="http://www.w3.org/2000/svg"><g id="Layer_2" data-name="Layer 2"><g id="russia" data-name="russia"><path id="path" d="m124.81 149.4a459 459 0 0 1 -97.62 0 27.69 27.69 0 0 1 -24.59-24.59 459 459 0 0 1 0-97.62 27.69 27.69 0 0 1 24.59-24.59 459 459 0 0 1 97.62 0 27.69 27.69 0 0 1 24.59 24.59 459 459 0 0 1 0 97.62 27.69 27.69 0 0 1 -24.59 24.59z" fill="#406bd4"/><path d="m151.38 52.26h-150.76q.63-12.54 2-25.08a27.68 27.68 0 0 1 24.57-24.58 459 459 0 0 1 97.62 0 27.68 27.68 0 0 1 24.59 24.58q1.33 12.53 1.98 25.08z" fill="#f0f9ff"/><path d="m151.38 99.73q-.63 12.54-2 25.08a27.68 27.68 0 0 1 -24.59 24.58 459 459 0 0 1 -97.62 0 27.68 27.68 0 0 1 -24.57-24.58q-1.34-12.54-2-25.08z" fill="#f40055"/></g></g></svg>
@@ -0,0 +1 @@
1
+ <svg height="512" viewBox="0 0 152 152" width="512" xmlns="http://www.w3.org/2000/svg"><g id="Layer_2" data-name="Layer 2"><g id="vietnam" data-name="vietnam"><path id="path" d="m124.81 149.4a459 459 0 0 1 -97.62 0 27.69 27.69 0 0 1 -24.59-24.59 459 459 0 0 1 0-97.62 27.69 27.69 0 0 1 24.59-24.59 459 459 0 0 1 97.62 0 27.69 27.69 0 0 1 24.59 24.59 459 459 0 0 1 0 97.62 27.69 27.69 0 0 1 -24.59 24.59z" fill="#f40055"/><g transform="translate(20,0)"><path d="m84.77 66.21a5 5 0 0 0 -4.77-3.46h-12a3.67 3.67 0 0 1 -3.5-2.54l-3.78-11.47a5 5 0 0 0 -9.53 0l-3.72 11.47a3.67 3.67 0 0 1 -3.47 2.54h-12.09a5 5 0 0 0 -2.91 9.07l9.76 7.08a3.69 3.69 0 0 1 1.3 4.1l-3.73 11.49a5 5 0 0 0 7.71 5.6l9.76-7.09a3.66 3.66 0 0 1 4.32 0l9.76 7.09a5 5 0 0 0 7.71-5.6l-3.73-11.49a3.68 3.68 0 0 1 1.34-4.1l9.8-7.08a5 5 0 0 0 1.82-5.61z" fill="#ffcb24"/></g></g></g></svg>
@@ -129,9 +129,9 @@ module.exports = async function (options) {
129
129
  checkLocality();
130
130
  }
131
131
 
132
- // Publish GH_TOKEN as repository secret
132
+ // Publish .env secrets as repository secrets
133
133
  if (options.publishGitHubToken) {
134
- await publishGitHubToken();
134
+ await publishSecrets();
135
135
  }
136
136
 
137
137
  // Deduplicate posts (remove duplicate posts with same slug but different dates)
@@ -389,7 +389,7 @@ function checkLocality() {
389
389
  }
390
390
  }
391
391
 
392
- async function publishGitHubToken() {
392
+ async function publishSecrets() {
393
393
  if (!process.env.GH_TOKEN) {
394
394
  logger.warn('GH_TOKEN not found in environment variables. Skipping secret publication.');
395
395
  return;
@@ -401,37 +401,67 @@ async function publishGitHubToken() {
401
401
  }
402
402
 
403
403
  if (Manager.isBuildMode()) {
404
- logger.log('Skipping GH_TOKEN publication in build mode.');
404
+ logger.log('Skipping secret publication in build mode.');
405
+ return;
406
+ }
407
+
408
+ // Read .env and collect all keys with non-empty values
409
+ const envPath = path.join(process.cwd(), '.env');
410
+ if (!jetpack.exists(envPath)) {
411
+ logger.warn('.env file not found. Skipping secret publication.');
412
+ return;
413
+ }
414
+
415
+ const envContent = jetpack.read(envPath);
416
+ const secrets = {};
417
+
418
+ envContent.split('\n').forEach(line => {
419
+ const trimmed = line.trim();
420
+ if (!trimmed || trimmed.startsWith('#')) {
421
+ return;
422
+ }
423
+ const match = trimmed.match(/^([A-Z_][A-Z0-9_]*)=["']?(.+?)["']?$/);
424
+ if (match && match[2]) {
425
+ secrets[match[1]] = match[2];
426
+ }
427
+ });
428
+
429
+ const secretNames = Object.keys(secrets);
430
+ if (!secretNames.length) {
431
+ logger.warn('No secrets with values found in .env. Skipping secret publication.');
405
432
  return;
406
433
  }
407
434
 
408
435
  try {
409
436
  const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
410
-
411
437
  const octokit = new Octokit({ auth: process.env.GH_TOKEN });
412
438
 
413
- logger.log(`Publishing GH_TOKEN as repository secret for ${owner}/${repo}...`);
439
+ logger.log(`Publishing ${secretNames.length} secret(s) for ${owner}/${repo}: ${secretNames.join(', ')}`);
414
440
 
415
441
  await sodium.ready;
416
442
 
417
443
  const { data: publicKeyData } = await octokit.actions.getRepoPublicKey({ owner, repo });
418
-
419
- const secretBytes = Buffer.from(process.env.GH_TOKEN);
420
444
  const keyBytes = Buffer.from(publicKeyData.key, 'base64');
421
- const encryptedBytes = sodium.crypto_box_seal(secretBytes, keyBytes);
422
- const encryptedValue = Buffer.from(encryptedBytes).toString('base64');
423
-
424
- await octokit.actions.createOrUpdateRepoSecret({
425
- owner,
426
- repo,
427
- secret_name: 'GH_TOKEN',
428
- encrypted_value: encryptedValue,
429
- key_id: publicKeyData.key_id,
430
- });
431
445
 
432
- logger.log(`Successfully published GH_TOKEN as repository secret`);
446
+ for (const [name, value] of Object.entries(secrets)) {
447
+ const secretBytes = Buffer.from(value);
448
+ const encryptedBytes = sodium.crypto_box_seal(secretBytes, keyBytes);
449
+ const encryptedValue = Buffer.from(encryptedBytes).toString('base64');
450
+
451
+ await octokit.actions.createOrUpdateRepoSecret({
452
+ owner,
453
+ repo,
454
+ secret_name: name,
455
+ encrypted_value: encryptedValue,
456
+ key_id: publicKeyData.key_id,
457
+ });
458
+
459
+ logger.log(` ✅ ${name}`);
460
+ }
461
+
462
+ logger.log(`Successfully published ${secretNames.length} secret(s)`);
433
463
  } catch (error) {
434
- logger.error(`Failed to publish GH_TOKEN as repository secret: ${error.message}`);
464
+ logger.error(`Failed to publish secrets: ${error.message}`);
435
465
  }
436
466
  }
437
467
 
@@ -18,8 +18,7 @@ concurrency:
18
18
  # contents: write
19
19
 
20
20
  env:
21
- GH_TOKEN: ${{ secrets.GH_TOKEN }}
22
- BACKEND_MANAGER_KEY: ${{ secrets.BACKEND_MANAGER_KEY }}
21
+ { github.secrets }
23
22
  RUBY_VERSION: '{ versions.ruby }'
24
23
  BUNDLER_VERSION: '{ versions.bundler }'
25
24
  NODE_VERSION: '{ versions.node }'
@@ -30,6 +30,19 @@ const ujConfig = jetpack.exists(ujConfigPath) ? JSON5.parse(jetpack.read(ujConfi
30
30
  // const cleanVersions = { versions: Manager.getCleanVersions()};
31
31
  const cleanVersions = { versions: package.engines };
32
32
 
33
+ // Build GitHub Actions secrets env block from default .env
34
+ const defaultEnvPath = path.join(rootPathPackage, 'dist/defaults/_.env');
35
+ const githubSecrets = (() => {
36
+ const content = jetpack.exists(defaultEnvPath) ? jetpack.read(defaultEnvPath) : '';
37
+ const lines = content.split('\n')
38
+ .map(l => l.trim())
39
+ .filter(l => l && !l.startsWith('#') && l.includes('='))
40
+ .map(l => l.split('=')[0].trim())
41
+ .map(key => `${key}: \${{ secrets.${key} }}`);
42
+
43
+ return { github: { secrets: lines.join('\n ') } };
44
+ })();
45
+
33
46
  // File MAP
34
47
  const FILE_MAP = {
35
48
  // Files to skip overwrite
@@ -116,7 +129,7 @@ const FILE_MAP = {
116
129
 
117
130
  // Files to run templating on
118
131
  '.github/workflows/build.yml': {
119
- template: { ...cleanVersions, ...ujConfig },
132
+ template: { ...cleanVersions, ...ujConfig, ...githubSecrets },
120
133
  },
121
134
  '.nvmrc': {
122
135
  template: cleanVersions,
@@ -158,7 +158,10 @@ function getSettings() {
158
158
  const projectThemePath = path.resolve(rootPathProject, 'src/assets/themes', config.theme.id);
159
159
  const ujmThemePath = path.resolve(rootPathPackage, 'dist/assets/themes', config.theme.id);
160
160
  // Use project theme if it exists, otherwise fall back to UJM theme
161
- return jetpack.exists(projectThemePath) ? projectThemePath : ujmThemePath;
161
+ const resolved = jetpack.exists(projectThemePath) ? projectThemePath : ujmThemePath;
162
+ // Ensure pages/ directory exists so dynamic imports don't fail
163
+ jetpack.dir(path.join(resolved, 'pages'));
164
+ return resolved;
162
165
  })(),
163
166
  },
164
167
  // Add module resolution paths for local web-manager
package/docs/icons.md CHANGED
@@ -117,6 +117,21 @@ $el.innerHTML = '<i class="fa-solid fa-check"></i> Text';
117
117
  $el.innerHTML = `${getPrerenderedIcon('circle-check', 'fa-sm me-1')} Text`;
118
118
  ```
119
119
 
120
+ ## Country Flag Icons
121
+
122
+ UJM ships rounded-square country flag SVGs at `assets/icons/flags/modern-square/`. The `{% uj_icon %}` tag resolves language codes to country flags via a `LANGUAGE_TO_COUNTRY` mapping in [jekyll-uj-powertools](https://github.com/itw-creative-works/jekyll-uj-powertools) (`lib/tags/icon.rb`). For example, `{% uj_icon "es" %}` resolves to `es.svg` (Spain), and `{% uj_icon "ja" %}` maps `ja` → `jp` → `jp.svg` (Japan).
123
+
124
+ Most flags are from a Flaticon rounded-square icon pack. The following 6 were hand-created to match the pack's style:
125
+
126
+ - `in.svg` (India) — saffron/white/green tricolor + Ashoka Chakra
127
+ - `ru.svg` (Russia) — white/blue/red tricolor
128
+ - `id.svg` (Indonesia) — red/white bicolor
129
+ - `vn.svg` (Vietnam) — red background + yellow star
130
+ - `pk.svg` (Pakistan) — green + white stripe, crescent & star
131
+ - `ph.svg` (Philippines) — blue/red + white triangle, sun & stars
132
+
133
+ When adding new flags, match the existing style: 152×152 viewBox, `clipPath` using the shared rounded-square path, flat/simplified design.
134
+
120
135
  ## Benefits
121
136
 
122
137
  - Icons are rendered server-side with proper Font Awesome classes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-jekyll-manager",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "description": "Ultimate Jekyll dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {