chore(deps): upgrade to Hugo v0.161.0 (css.TailwindCSS Node.js sandbox)

Hugo v0.161 dropped the standalone tailwindcss binary; @tailwindcss/cli
npm package is now the only accepted transformer, invoked via
`node --permission` (Node >= 22 required).

- Bump module.hugoVersion.min to 0.161.0 in modules/blox/hugo.yaml;
  add comment explaining why security.node.permissions is left to
  Hugo's built-in defaults
- Bump hugo_version / HUGO_VERSION / devcontainer image tag to 0.161.0
  across all 9 templates (hugoblox.yaml, netlify.toml, devcontainer.json)
- Bump devcontainer base image Node 20 → 22 and default Hugo version
- Add scripts/check-template-deps.mjs to assert every template declares
  tailwindcss + @tailwindcss/cli; wire as pnpm check:template-deps
- Harden CI: new audit-template-deps job gates all build jobs; add
  `require.resolve('@tailwindcss/cli/package.json')` check after each
  pnpm install to catch the exact "binary is not a Node.js script" failure
This commit is contained in:
George Cushen committed 2026-04-30 20:12:37 +01:00
1 parent a034cf7dbe
commit 6aa2898258
31 files changed
+125 -31

No files matched your search

+2 -2
View File
@@ -3,9 +3,9 @@
FROM mcr.microsoft.com/devcontainers/go:1.22-bookworm
ARG NODE_VERSION=20
ARG NODE_VERSION=22
ARG PNPM_VERSION=10.14.0
ARG HUGO_VERSION=0.152.2
ARG HUGO_VERSION=0.161.0
ENV DEBIAN_FRONTEND=noninteractive
ENV PNPM_HOME=/home/vscode/.local/share/pnpm
@@ -43,7 +43,25 @@ env:
NODE_VERSION: "22"
jobs:
audit-template-deps:
name: Audit template Tailwind dependencies
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "${{ env.NODE_VERSION }}"
- name: Verify every template declares @tailwindcss/cli + tailwindcss
# Hugo >= 0.161.0 requires the npm @tailwindcss/cli package; the
# standalone tailwindcss binary is no longer accepted.
run: node scripts/check-template-deps.mjs
build-templates:
needs: audit-template-deps
runs-on: ubuntu-latest
strategy:
fail-fast: false
@@ -99,6 +117,13 @@ jobs:
# and may not have lock files checked into the repo
run: pnpm install --no-frozen-lockfile
- name: Verify @tailwindcss/cli resolves via node_modules
# Hugo >= 0.161.0 invokes node @tailwindcss/cli/dist/index.mjs directly.
# If require.resolve fails here, Hugo will fall back to PATH and emit
# "binary tailwindcss is not a Node.js script" at build time.
working-directory: ${{ matrix.starter.path }}
run: node -e "require.resolve('@tailwindcss/cli/package.json')"
- name: Read Hugo version from template
id: hugo
run: |
@@ -123,6 +148,7 @@ jobs:
smoke-test-latest:
name: Test site on latest Hugo (allowed to fail)
needs: audit-template-deps
runs-on: ubuntu-latest
continue-on-error: true
steps:
@@ -148,6 +174,10 @@ jobs:
working-directory: test
run: pnpm install --no-frozen-lockfile
- name: Verify @tailwindcss/cli resolves via node_modules
working-directory: test
run: node -e "require.resolve('@tailwindcss/cli/package.json')"
- name: Cache Hugo resources (test)
uses: actions/cache@v5
with:
@@ -168,6 +198,7 @@ jobs:
canary-academic-latest:
name: academic-cv on latest Hugo (allowed to fail)
needs: audit-template-deps
runs-on: ubuntu-latest
continue-on-error: true
steps:
@@ -190,6 +221,10 @@ jobs:
working-directory: templates/academic-cv
run: pnpm install --no-frozen-lockfile
- name: Verify @tailwindcss/cli resolves via node_modules
working-directory: templates/academic-cv
run: node -e "require.resolve('@tailwindcss/cli/package.json')"
- name: Cache Hugo resources (template)
uses: actions/cache@v5
with:
@@ -210,6 +245,7 @@ jobs:
smoke-test-min:
name: Test site on minimum supported Hugo
needs: audit-template-deps
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -234,6 +270,10 @@ jobs:
working-directory: test
run: pnpm install --no-frozen-lockfile
- name: Verify @tailwindcss/cli resolves via node_modules
working-directory: test
run: node -e "require.resolve('@tailwindcss/cli/package.json')"
- name: Read minimum Hugo version from framework
id: min
run: |
+3 -2
View File
@@ -14,8 +14,9 @@ on:
env:
IMAGE_NAME: ghcr.io/hugoblox/hugo-blox-dev
DEFAULT_HUGO_VERSION: 0.152.2
NODE_VERSION: 20
# Hugo >= 0.161.0 requires Node >= 22 for css.TailwindCSS Node permissions.
DEFAULT_HUGO_VERSION: 0.161.0
NODE_VERSION: 22
PNPM_VERSION: 10.14.0
jobs:
+9 -1
View File
@@ -72,6 +72,11 @@ security:
- ^HUGO_
# Allow continuous integration vars
- ^CI$
# Note: security.node.permissions is intentionally NOT set here. Hugo's
# built-in defaults already permit `tailwindcss` for allowAddons,
# allowChildProcess, and allowWorker (see https://gohugo.io/configuration/security/).
# Restating the defaults would create maintenance debt if upstream evolves them
# (e.g. when @tailwindcss/oxide native bindings need new permissions).
outputFormats:
backlinks:
mediaType: application/json
@@ -103,7 +108,10 @@ params:
address_format: en-us
module:
hugoVersion:
min: "0.160.0"
# 0.161.0 introduced css.TailwindCSS Node.js permission sandbox
# (Node >= 22 required) and dropped support for the standalone
# tailwindcss binary. The npm @tailwindcss/cli package is required.
min: "0.161.0"
extended: true
imports:
- path: github.com/HugoBlox/kit/modules/analytics
+2 -1
View File
@@ -41,7 +41,8 @@
"vendor:libs": "vite build --config vite.config.js",
"vendor:libs:watch": "vite build --config vite.config.js --watch",
"vendor:update-and-build": "pnpm up katex mermaid markmap-autoloader alpinejs plotly.js preact --latest && pnpm vendor:libs",
"test:releaser": "poetry run python scripts/test_release_modules.py"
"test:releaser": "poetry run python scripts/test_release_modules.py",
"check:template-deps": "node scripts/check-template-deps.mjs"
},
"browserslist": "> 1%"
}
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env node
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join, resolve } from "node:path";
const REQUIRED = ["tailwindcss", "@tailwindcss/cli"];
const TEMPLATES_DIR = resolve(process.argv[2] ?? "templates");
const failures = [];
const checked = [];
for (const name of readdirSync(TEMPLATES_DIR).sort()) {
const dir = join(TEMPLATES_DIR, name);
if (!statSync(dir).isDirectory()) continue;
const pkgPath = join(dir, "package.json");
let pkg;
try {
pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
} catch (err) {
if (err.code === "ENOENT") continue;
failures.push(`${name}: package.json unreadable — ${err.message}`);
continue;
}
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
const missing = REQUIRED.filter((dep) => !(dep in deps));
if (missing.length) {
failures.push(`${name}: missing ${missing.join(", ")}`);
} else {
checked.push(name);
}
}
if (failures.length) {
console.error("✗ Template dependency audit failed:\n");
for (const f of failures) console.error(` ${f}`);
console.error(
`\nHugo >= 0.161.0 requires the npm @tailwindcss/cli package — the standalone tailwindcss binary is no longer accepted.`,
);
process.exit(1);
}
console.log(`✓ All ${checked.length} templates declare required Tailwind dependencies:`);
for (const name of checked) console.log(` ${name}`);
@@ -1,6 +1,6 @@
{
"name": "HugoBlox Codespace",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.159.2",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.161.0",
"updateContentCommand": "pnpm install --frozen-lockfile --prefer-offline",
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
+1 -1
View File
@@ -1,5 +1,5 @@
build:
hugo_version: '0.159.2'
hugo_version: '0.161.0'
deploy:
# Deployment target: github-pages, netlify, vercel, cloudflare, or none
host: 'github-pages'
+1 -1
View File
@@ -21,7 +21,7 @@
publish = "public"
[build.environment]
HUGO_VERSION = "0.159.2"
HUGO_VERSION = "0.161.0"
GO_VERSION = "1.21.5"
NODE_VERSION = "22"
# Netlify runs an implicit install step; ensure it never enforces frozen lockfiles
@@ -1,6 +1,6 @@
{
"name": "HugoBlox Codespace",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.159.2",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.161.0",
"updateContentCommand": "pnpm install --frozen-lockfile --prefer-offline",
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
+1 -1
View File
@@ -1,5 +1,5 @@
build:
hugo_version: '0.159.2'
hugo_version: '0.161.0'
deploy:
# Deployment target: github-pages, netlify, vercel, cloudflare, or none
host: 'github-pages'
+1 -1
View File
@@ -21,7 +21,7 @@
publish = "public"
[build.environment]
HUGO_VERSION = "0.159.2"
HUGO_VERSION = "0.161.0"
GO_VERSION = "1.21.5"
NODE_VERSION = "22"
# Ensure Netlify's implicit install does not force a frozen lockfile
@@ -1,6 +1,6 @@
{
"name": "HugoBlox Codespace",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.159.2",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.161.0",
"updateContentCommand": "pnpm install --frozen-lockfile --prefer-offline",
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
+1 -1
View File
@@ -1,5 +1,5 @@
build:
hugo_version: '0.159.2'
hugo_version: '0.161.0'
deploy:
# Deployment target: github-pages, netlify, vercel, cloudflare, or none
host: 'github-pages'
+1 -1
View File
@@ -21,7 +21,7 @@
publish = "public"
[build.environment]
HUGO_VERSION = "0.159.2"
HUGO_VERSION = "0.161.0"
GO_VERSION = "1.21.5"
NODE_VERSION = "22"
# Netlify runs an implicit install step; ensure it never enforces frozen lockfiles
@@ -1,6 +1,6 @@
{
"name": "HugoBlox Codespace",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.159.2",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.161.0",
"updateContentCommand": "pnpm install --frozen-lockfile --prefer-offline",
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
+1 -1
View File
@@ -1,5 +1,5 @@
build:
hugo_version: '0.159.2'
hugo_version: '0.161.0'
deploy:
# Deployment target: github-pages, netlify, vercel, cloudflare, or none
host: 'github-pages'
+1 -1
View File
@@ -21,7 +21,7 @@
publish = "public"
[build.environment]
HUGO_VERSION = "0.159.2"
HUGO_VERSION = "0.161.0"
GO_VERSION = "1.21.5"
NODE_VERSION = "22"
# Ensure Netlify's implicit install does not enforce frozen lockfiles
@@ -1,6 +1,6 @@
{
"name": "HugoBlox Codespace",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.159.2",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.161.0",
"updateContentCommand": "pnpm install --frozen-lockfile --prefer-offline",
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
+1 -1
View File
@@ -1,5 +1,5 @@
build:
hugo_version: '0.159.2'
hugo_version: '0.161.0'
deploy:
# Deployment target: github-pages, netlify, vercel, cloudflare, or none
host: 'github-pages'
+1 -1
View File
@@ -21,7 +21,7 @@
publish = "public"
[build.environment]
HUGO_VERSION = "0.159.2"
HUGO_VERSION = "0.161.0"
GO_VERSION = "1.21.5"
NODE_VERSION = "22"
# Ensure Netlify's implicit install step never enforces frozen lockfiles
@@ -1,6 +1,6 @@
{
"name": "HugoBlox Codespace",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.159.2",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.161.0",
"updateContentCommand": "pnpm install --frozen-lockfile --prefer-offline",
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
+1 -1
View File
@@ -1,5 +1,5 @@
build:
hugo_version: '0.159.2'
hugo_version: '0.161.0'
deploy:
# Deployment target: github-pages, netlify, vercel, cloudflare, or none
host: 'github-pages'
+1 -1
View File
@@ -20,7 +20,7 @@
publish = "public"
[build.environment]
HUGO_VERSION = "0.159.2"
HUGO_VERSION = "0.161.0"
GO_VERSION = "1.21.5"
NODE_VERSION = "22"
PNPM_FLAGS = "--no-frozen-lockfile"
@@ -1,6 +1,6 @@
{
"name": "HugoBlox Codespace",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.159.2",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.161.0",
"updateContentCommand": "pnpm install --frozen-lockfile --prefer-offline",
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
+1 -1
View File
@@ -1,5 +1,5 @@
build:
hugo_version: '0.159.2'
hugo_version: '0.161.0'
deploy:
# Deployment target: github-pages, netlify, vercel, cloudflare, or none
host: 'github-pages'
+1 -1
View File
@@ -21,7 +21,7 @@
publish = "public"
[build.environment]
HUGO_VERSION = "0.159.2"
HUGO_VERSION = "0.161.0"
GO_VERSION = "1.21.5"
NODE_VERSION = "22"
# Ensure Netlify's implicit install step does not enforce frozen lockfiles
+1 -1
View File
@@ -1,5 +1,5 @@
build:
hugo_version: '0.159.2'
hugo_version: '0.161.0'
deploy:
# Deployment target: github-pages, netlify, vercel, cloudflare, or none
host: 'github-pages'
@@ -1,6 +1,6 @@
{
"name": "HugoBlox Codespace",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.159.2",
"image": "ghcr.io/HugoBlox/hugo-blox-dev:hugo0.161.0",
"updateContentCommand": "pnpm install --frozen-lockfile --prefer-offline",
"postCreateCommand": "pnpm --version && hugo version",
"customizations": {
+1 -1
View File
@@ -1,5 +1,5 @@
build:
hugo_version: '0.159.2'
hugo_version: '0.161.0'
deploy:
# Deployment target: github-pages, netlify, vercel, cloudflare, or none
host: 'github-pages'
+1 -1
View File
@@ -21,7 +21,7 @@
publish = "public"
[build.environment]
HUGO_VERSION = "0.159.2"
HUGO_VERSION = "0.161.0"
GO_VERSION = "1.21.5"
NODE_VERSION = "22"
# Ensure Netlify's implicit install step does not enforce frozen lockfiles