/Hugo-Update
/hugo-update is a Claude Code slash command for this blog. It reads the target Hugo version from .hugo-version and syncs the HUGO_VERSION environment variable in Cloudflare Pages — verifying a local build first, then updating preview and waiting for human confirmation before updating production. All Cloudflare API calls are handled by scripts/hugo-update.sh.
| Step | Action |
|---|---|
| Check current state | Runs scripts/hugo-update.sh status to show preview and production versions vs. the target |
| Verify local build | Starts hugo serve locally to confirm the site builds with the target version |
| Update preview | Runs scripts/hugo-update.sh preview — patches the preview env var and triggers a deployment |
| Verify preview | Presents the preview URL for human verification before touching production |
| Update production | Runs scripts/hugo-update.sh production — patches the production env var |
| Summary | Reports what changed; new version takes effect on next production deployment |
Command Definition Link to heading
The content below is included directly from .claude/commands/hugo-update.md at build time — edit that file to update both this page and Claude Code’s behavior simultaneously.
This file is published at /hugo-update/ via Hugo’s
readFileshortcode. Changes here are automatically reflected on the page at next build.
Updates the HUGO_VERSION environment variable in Cloudflare Pages by reading the target version from .hugo-version and applying it to preview, then production after human verification. All Cloudflare API interactions are handled by scripts/hugo-update.sh.
How It Works Link to heading
A single bash script — scripts/hugo-update.sh — handles all Cloudflare API calls. The script fetches the API token from 1Password CLI (op) once per invocation, discovers the Cloudflare account and Pages project via API (no hardcoded IDs), and performs the requested action.
./scripts/hugo-update.sh status # Show current vs target versions
./scripts/hugo-update.sh preview # Update preview env var and trigger a build
./scripts/hugo-update.sh production # Update production env var
Prerequisites Link to heading
- 1Password CLI (
op) installed and authenticated curlandjqavailable
Workflow Link to heading
Step 1: Check current state Link to heading
./scripts/hugo-update.sh status
Show the output to the user. If the script exits with “Already up to date”, stop.
Step 2: Verify local build Link to heading
Start the local development server to confirm the site builds with the target Hugo version:
hugo serve --minify --enableGitInfo --baseURL http://localhost:1313
Wait for the user to confirm the local build looks correct before continuing.
Step 3: Update preview Link to heading
./scripts/hugo-update.sh preview
Show the preview URL from the output. Ask the user to verify the preview deployment looks correct before proceeding to production.
Step 4: Update production Link to heading
After the user confirms the preview is correct:
./scripts/hugo-update.sh production
Report the result. The new Hugo version takes effect on the next production deployment (merge to main).
Step 5: Summary Link to heading
Report what changed: preview and production HUGO_VERSION before and after.
GitHub Actions Pipeline Link to heading
A companion workflow — .github/workflows/hugo-update.yml — runs weekly and opens a pull request automatically when a new Hugo release is available. The /hugo-update command handles the Cloudflare side of that PR: syncing the HUGO_VERSION environment variable to match the updated .hugo-version file.
Script Link to heading
The shell script that handles all Cloudflare API calls (scripts/hugo-update.sh):
#!/usr/bin/env bash
set -euo pipefail
HUGO_VERSION_FILE=".hugo-version"
die() {
echo "ERROR: $*" >&2
exit 1
}
usage() {
echo "Usage: $(basename "$0") <status|preview|production>" >&2
echo "" >&2
echo " status Show current vs target HUGO_VERSION in Cloudflare Pages" >&2
echo " preview Update preview env var and trigger a preview deployment" >&2
echo " production Update production env var (takes effect on next main deployment)" >&2
exit 1
}
read_target_version() {
[[ -f "$HUGO_VERSION_FILE" ]] || die ".hugo-version not found"
local version
version=$(tr -d '[:space:]' < "$HUGO_VERSION_FILE")
[[ -n "$version" ]] || die ".hugo-version is empty"
echo "$version"
}
fetch_token() {
local token
token=$(op read 'op://claude/Cloudflare Workers/cloudflare_api_token' 2>/dev/null) || \
die "Failed to read token from 1Password. Is 'op' installed and authenticated?"
[[ -n "$token" ]] || die "Token from 1Password is empty"
echo "$token"
}
discover_account() {
local token="$1"
local response count
response=$(curl -sf https://api.cloudflare.com/client/v4/accounts \
-H "Authorization: Bearer $token") || die "Failed to reach Cloudflare API"
count=$(echo "$response" | jq '.result | length')
[[ "$count" -eq 1 ]] || die "Expected 1 Cloudflare account, found $count"
echo "$response" | jq -r '.result[0].id'
}
discover_project() {
local token="$1"
local account_id="$2"
local response count
response=$(curl -sf "https://api.cloudflare.com/client/v4/accounts/$account_id/pages/projects" \
-H "Authorization: Bearer $token") || die "Failed to list Pages projects"
count=$(echo "$response" | jq '.result | length')
[[ "$count" -eq 1 ]] || die "Expected 1 Pages project, found $count"
echo "$response" | jq -r '.result[0].name'
}
get_versions() {
local token="$1"
local account_id="$2"
local project="$3"
local response
response=$(curl -sf "https://api.cloudflare.com/client/v4/accounts/$account_id/pages/projects/$project" \
-H "Authorization: Bearer $token") || die "Failed to fetch project details"
echo "$response" | jq -r '{
preview: (.result.deployment_configs.preview.env_vars.HUGO_VERSION.value // "unset"),
production: (.result.deployment_configs.production.env_vars.HUGO_VERSION.value // "unset")
}'
}
patch_env_var() {
local token="$1"
local account_id="$2"
local project="$3"
local env="$4"
local version="$5"
local result success
result=$(curl -sf -X PATCH \
"https://api.cloudflare.com/client/v4/accounts/$account_id/pages/projects/$project" \
-H "Authorization: Bearer $token" \
-H "Content-Type: application/json" \
--data "$(jq -n \
--arg env "$env" \
--arg ver "$version" \
'{deployment_configs: {($env): {env_vars: {HUGO_VERSION: {value: $ver, type: "plain_text"}}}}}'
)") || die "PATCH request failed for $env environment"
success=$(echo "$result" | jq -r '.success')
[[ "$success" == "true" ]] || die "Update failed: $(echo "$result" | jq -c '.errors')"
}
trigger_deployment() {
local token="$1"
local account_id="$2"
local project="$3"
local branch="$4"
local version="$5"
local response
response=$(curl -sf -X POST \
"https://api.cloudflare.com/client/v4/accounts/$account_id/pages/projects/$project/deployments" \
-H "Authorization: Bearer $token" \
-H "Content-Type: application/json" \
--data "$(jq -n --arg branch "$branch" --arg ver "$version" \
'{branch: $branch, env_vars: {HUGO_VERSION: {value: $ver, type: "plain_text"}}}')") || \
die "Failed to trigger deployment"
echo "$response"
}
cmd_status() {
local target token account_id project versions preview_ver production_ver
target=$(read_target_version)
token=$(fetch_token)
account_id=$(discover_account "$token")
project=$(discover_project "$token" "$account_id")
versions=$(get_versions "$token" "$account_id" "$project")
preview_ver=$(echo "$versions" | jq -r '.preview')
production_ver=$(echo "$versions" | jq -r '.production')
printf "%-12s %-10s %s\n" "Environment" "Current" "Target"
printf "%-12s %-10s %s\n" "-----------" "-------" "------"
printf "%-12s %-10s %s\n" "preview" "$preview_ver" "$target"
printf "%-12s %-10s %s\n" "production" "$production_ver" "$target"
if [[ "$preview_ver" == "$target" && "$production_ver" == "$target" ]]; then
echo ""
echo "Already up to date."
exit 2
fi
}
cmd_preview() {
local target token account_id project versions preview_ver branch response deploy_url deploy_id deploy_hugo dashboard_url
target=$(read_target_version)
token=$(fetch_token)
account_id=$(discover_account "$token")
project=$(discover_project "$token" "$account_id")
versions=$(get_versions "$token" "$account_id" "$project")
preview_ver=$(echo "$versions" | jq -r '.preview')
if [[ "$preview_ver" == "$target" ]]; then
echo "Preview already at Hugo $target."
exit 2
fi
patch_env_var "$token" "$account_id" "$project" "preview" "$target"
branch=$(git branch --show-current)
response=$(trigger_deployment "$token" "$account_id" "$project" "$branch" "$target")
deploy_url=$(echo "$response" | jq -r '.result.url')
deploy_id=$(echo "$response" | jq -r '.result.id')
deploy_hugo=$(echo "$response" | jq -r '.result.env_vars.HUGO_VERSION.value // "unset"')
dashboard_url="https://dash.cloudflare.com/$account_id/pages/view/$project/$deploy_id"
echo "Updated preview: $preview_ver → $target"
echo "Dashboard: $dashboard_url"
echo "Preview: $deploy_url"
if [[ "$deploy_hugo" != "$target" ]]; then
echo "WARNING: Deployment env var is '$deploy_hugo', expected '$target'" >&2
exit 1
fi
echo "Verified: deployment will use Hugo $deploy_hugo"
}
cmd_production() {
local target token account_id project versions production_ver
target=$(read_target_version)
token=$(fetch_token)
account_id=$(discover_account "$token")
project=$(discover_project "$token" "$account_id")
versions=$(get_versions "$token" "$account_id" "$project")
production_ver=$(echo "$versions" | jq -r '.production')
if [[ "$production_ver" == "$target" ]]; then
echo "Production already at Hugo $target."
exit 2
fi
patch_env_var "$token" "$account_id" "$project" "production" "$target"
echo "Updated production: $production_ver → $target"
echo "Hugo $target takes effect on the next production deployment (merge to main)."
}
case "${1:-}" in
status) cmd_status ;;
preview) cmd_preview ;;
production) cmd_production ;;
*) usage ;;
esac