Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/deploy-pr-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,51 @@ jobs:
echo " - Total docs: $TOTAL_CONTENT"
find static/images -type f 2>/dev/null | wc -l | xargs echo " - Images:"

- name: Restore hand-maintained i18n files
# The content-provisioning step above may have overlaid i18n/ from the
# content branch (or regenerated it from Notion), silently discarding
# the hand-maintained theme translation files this repo owns
# (docusaurus-theme-classic/*). Restore them unconditionally from the
# checked-out PR ref so the preview renders what the PR is actually
# proposing, not unrelated content-branch state.
#
# code.json also carries Notion-generated, content-only translation
# keys that live only on the content branch. A blind `checkout HEAD`
# here would silently drop those on every build. Merge instead: keep
# content-only keys, let HEAD win any key this repo also defines.
run: |
set -e
git ls-files -z 'i18n/*/docusaurus-theme-classic/*' \
| xargs -0 -r git checkout HEAD --

# `jq -s` slurps every JSON document across BOTH input files into
# one flat array and blindly indexes [0]/[1] — it does not verify
# each file actually contributed exactly one object. An empty or
# `null` content file silently falls back to HEAD alone; a content
# file with two concatenated JSON documents silently drops HEAD
# entirely. Validate each side is exactly one JSON object before
# merging so malformed input fails the step instead of shipping a
# silently wrong catalog.
validate_single_json_object() {
local file="$1" label="$2" count
count=$(jq -s 'length' "$file" 2>/dev/null) || { echo "::error::$label: not valid JSON" >&2; return 1; }
if [ "$count" != "1" ]; then
echo "::error::$label: expected exactly 1 JSON document, found $count" >&2
return 1
fi
jq -e 'type == "object"' "$file" >/dev/null 2>&1 || { echo "::error::$label: root value is not a JSON object" >&2; return 1; }
}

git ls-files -z 'i18n/*/code.json' | while IFS= read -r -d '' f; do
head_tmp=$(mktemp)
git show "HEAD:$f" > "$head_tmp"
validate_single_json_object "$f" "$f (content overlay)"
validate_single_json_object "$head_tmp" "HEAD:$f"
jq -s '.[0] + .[1]' "$f" "$head_tmp" > "$f.tmp"
mv "$f.tmp" "$f"
rm -f "$head_tmp"
done

- name: Build documentation
# IS_PRODUCTION not set - generates noindex meta tags and disallow robots.txt
run: bun run build
Expand Down
53 changes: 52 additions & 1 deletion .github/workflows/deploy-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,50 @@ jobs:
echo "📂 Checking out content at SHA ${SHA:0:8}..."
git checkout "$SHA" -- docs/ i18n/ static/images/

# The checkout above overlays i18n/ from the locked content SHA,
# which does not carry this repo's hand-maintained theme
# translation files (docusaurus-theme-classic/*) — those live
# only on main. Restore them from main so the theme chrome
# translations this repo owns actually reach production instead
# of silently falling back to whatever (or nothing) the content
# SHA has for those paths.
git ls-files -z 'i18n/*/docusaurus-theme-classic/*' \
| xargs -0 -r git checkout HEAD --

# code.json also carries Notion-generated, content-only
# translation keys that live only on the content SHA. A blind
# `checkout HEAD` here would silently drop those on every build.
# Merge instead: keep content-only keys, let HEAD win any key
# this repo also defines.
#
# `jq -s` slurps every JSON document across BOTH input files into
# one flat array and blindly indexes [0]/[1] — it does not verify
# each file actually contributed exactly one object. An empty or
# `null` content file silently falls back to HEAD alone; a content
# file with two concatenated JSON documents silently drops HEAD
# entirely. Validate each side is exactly one JSON object before
# merging so malformed input fails the step instead of shipping a
# silently wrong catalog.
validate_single_json_object() {
local file="$1" label="$2" count
count=$(jq -s 'length' "$file" 2>/dev/null) || { echo "::error::$label: not valid JSON" >&2; return 1; }
if [ "$count" != "1" ]; then
echo "::error::$label: expected exactly 1 JSON document, found $count" >&2
return 1
fi
jq -e 'type == "object"' "$file" >/dev/null 2>&1 || { echo "::error::$label: root value is not a JSON object" >&2; return 1; }
}

git ls-files -z 'i18n/*/code.json' | while IFS= read -r -d '' f; do
head_tmp=$(mktemp)
git show "HEAD:$f" > "$head_tmp"
validate_single_json_object "$f" "$f (content overlay)"
validate_single_json_object "$head_tmp" "HEAD:$f"
jq -s '.[0] + .[1]' "$f" "$head_tmp" > "$f.tmp"
mv "$f.tmp" "$f"
rm -f "$head_tmp"
done

# Validate content exists
echo "🔍 Validating content..."

Expand Down Expand Up @@ -220,7 +264,14 @@ jobs:
git config user.email "github-actions[bot]@users.noreply.github.com"
echo "${SHA}" > content-lock.sha
git add content-lock.sha
git commit -m "chore(content): promote content ${SHA:0:8} to production [skip ci]"

# Earlier steps in this job overlay content into i18n/ and merge
# code.json in the working tree only, without re-staging it — the
# index still holds the raw content-branch (unmerged) catalog. A
# plain `git commit` here would commit that whole index snapshot,
# silently regressing code.json on main. Scope the commit to
# content-lock.sha only so any other staged changes are ignored.
git commit content-lock.sha -m "chore(content): promote content ${SHA:0:8} to production [skip ci]"
git push origin HEAD:main
echo "✅ Updated content-lock.sha → ${SHA:0:8}"

Expand Down
46 changes: 45 additions & 1 deletion .github/workflows/deploy-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:

if [ "$SHOULD_DEPLOY" != "true" ]; then
if [ "$BRANCH_NAME" = "main" ]; then
if printf '%s\n' "$CHANGED_FILES" | grep -Eq '^(src/|static/img/|docusaurus\.config\.ts$|sidebars\.ts$|package\.json$)'; then
if printf '%s\n' "$CHANGED_FILES" | grep -Eq '^(src/|static/img/|docusaurus\.config\.ts$|sidebars\.ts$|package\.json$|docs/|i18n/)'; then
SHOULD_DEPLOY="true"
REASON="main push includes code/config paths."
else
Expand Down Expand Up @@ -124,6 +124,50 @@ jobs:
echo "📂 Checking out content files..."
git checkout origin/content -- docs/ i18n/ static/images/

# The checkout above overlays i18n/ from the content branch,
# which does not carry this repo's hand-maintained theme
# translation files (docusaurus-theme-classic/*) — those live
# only on main. Restore them from main so the theme chrome
# translations this repo owns actually reach staging instead of
# silently falling back to whatever (or nothing) the content
# branch has for those paths.
git ls-files -z 'i18n/*/docusaurus-theme-classic/*' \
| xargs -0 -r git checkout HEAD --

# code.json also carries Notion-generated, content-only
# translation keys that live only on the content branch. A blind
# `checkout HEAD` here would silently drop those on every build.
# Merge instead: keep content-only keys, let HEAD win any key
# this repo also defines.
#
# `jq -s` slurps every JSON document across BOTH input files into
# one flat array and blindly indexes [0]/[1] — it does not verify
# each file actually contributed exactly one object. An empty or
# `null` content file silently falls back to HEAD alone; a content
# file with two concatenated JSON documents silently drops HEAD
# entirely. Validate each side is exactly one JSON object before
# merging so malformed input fails the step instead of shipping a
# silently wrong catalog.
validate_single_json_object() {
local file="$1" label="$2" count
count=$(jq -s 'length' "$file" 2>/dev/null) || { echo "::error::$label: not valid JSON" >&2; return 1; }
if [ "$count" != "1" ]; then
echo "::error::$label: expected exactly 1 JSON document, found $count" >&2
return 1
fi
jq -e 'type == "object"' "$file" >/dev/null 2>&1 || { echo "::error::$label: root value is not a JSON object" >&2; return 1; }
}

git ls-files -z 'i18n/*/code.json' | while IFS= read -r -d '' f; do
head_tmp=$(mktemp)
git show "HEAD:$f" > "$head_tmp"
validate_single_json_object "$f" "$f (content overlay)"
validate_single_json_object "$head_tmp" "HEAD:$f"
jq -s '.[0] + .[1]' "$f" "$head_tmp" > "$f.tmp"
mv "$f.tmp" "$f"
rm -f "$head_tmp"
done

# Validate content exists
echo "🔍 Validating content..."

Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/deploy-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,50 @@ jobs:
echo "📂 Checking out content files..."
git checkout origin/content -- docs/ i18n/ static/images/

# The checkout above overlays i18n/ from the content branch,
# which does not carry this repo's hand-maintained theme
# translation files (docusaurus-theme-classic/*) — those live
# only on main. Restore them from main so the theme chrome
# translations this repo owns actually reach the test deployment
# instead of silently falling back to whatever (or nothing) the
# content branch has for those paths.
git ls-files -z 'i18n/*/docusaurus-theme-classic/*' \
| xargs -0 -r git checkout HEAD --

# code.json also carries Notion-generated, content-only
# translation keys that live only on the content branch. A blind
# `checkout HEAD` here would silently drop those on every build.
# Merge instead: keep content-only keys, let HEAD win any key
# this repo also defines.
#
# `jq -s` slurps every JSON document across BOTH input files into
# one flat array and blindly indexes [0]/[1] — it does not verify
# each file actually contributed exactly one object. An empty or
# `null` content file silently falls back to HEAD alone; a content
# file with two concatenated JSON documents silently drops HEAD
# entirely. Validate each side is exactly one JSON object before
# merging so malformed input fails the step instead of shipping a
# silently wrong catalog.
validate_single_json_object() {
local file="$1" label="$2" count
count=$(jq -s 'length' "$file" 2>/dev/null) || { echo "::error::$label: not valid JSON" >&2; return 1; }
if [ "$count" != "1" ]; then
echo "::error::$label: expected exactly 1 JSON document, found $count" >&2
return 1
fi
jq -e 'type == "object"' "$file" >/dev/null 2>&1 || { echo "::error::$label: root value is not a JSON object" >&2; return 1; }
}

git ls-files -z 'i18n/*/code.json' | while IFS= read -r -d '' f; do
head_tmp=$(mktemp)
git show "HEAD:$f" > "$head_tmp"
validate_single_json_object "$f" "$f (content overlay)"
validate_single_json_object "$head_tmp" "HEAD:$f"
jq -s '.[0] + .[1]' "$f" "$head_tmp" > "$f.tmp"
mv "$f.tmp" "$f"
rm -f "$head_tmp"
done

# Validate content exists
echo "🔍 Validating content..."

Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,56 @@ jobs:
if git checkout origin/content -- i18n/; then
echo "has_i18n=true" >> "$GITHUB_OUTPUT"
echo "✅ Loaded i18n content from origin/content."

# The checkout above overlays i18n/ from the content branch,
# which would silently discard the hand-maintained theme
# translation files this repo owns (docusaurus-theme-classic/*),
# letting locale tests pass against stale content-branch data
# instead of what's actually committed here. A prior version of
# this restore only looked at what the current ref changed
# relative to its merge-base with main, which is empty on a push
# to main itself (HEAD == origin/main) and for any PR that
# doesn't touch i18n/ — so it silently did nothing on exactly
# the runs that matter most. Restore unconditionally instead:
# these paths are always supposed to come from the git-tracked
# ref being tested, never from content.
git ls-files -z 'i18n/*/docusaurus-theme-classic/*' \
| xargs -0 -r git checkout HEAD --

# code.json also carries Notion-generated, content-only
# translation keys that live only on the content branch. A
# blind `checkout HEAD` here would silently drop those and let
# locale tests validate an unrealistic, content-key-free
# catalog. Merge instead: keep content-only keys, let HEAD win
# any key this repo also defines.
#
# `jq -s` slurps every JSON document across BOTH input files
# into one flat array and blindly indexes [0]/[1] — it does not
# verify each file actually contributed exactly one object. An
# empty or `null` content file silently falls back to HEAD
# alone; a content file with two concatenated JSON documents
# silently drops HEAD entirely. Validate each side is exactly
# one JSON object before merging so malformed input fails the
# step instead of shipping a silently wrong catalog.
validate_single_json_object() {
local file="$1" label="$2" count
count=$(jq -s 'length' "$file" 2>/dev/null) || { echo "::error::$label: not valid JSON" >&2; return 1; }
if [ "$count" != "1" ]; then
echo "::error::$label: expected exactly 1 JSON document, found $count" >&2
return 1
fi
jq -e 'type == "object"' "$file" >/dev/null 2>&1 || { echo "::error::$label: root value is not a JSON object" >&2; return 1; }
}

git ls-files -z 'i18n/*/code.json' | while IFS= read -r -d '' f; do
head_tmp=$(mktemp)
git show "HEAD:$f" > "$head_tmp"
validate_single_json_object "$f" "$f (content overlay)"
validate_single_json_object "$head_tmp" "HEAD:$f"
jq -s '.[0] + .[1]' "$f" "$head_tmp" > "$f.tmp"
mv "$f.tmp" "$f"
rm -f "$head_tmp"
done
else
echo "has_i18n=false" >> "$GITHUB_OUTPUT"
echo "⚠️ origin/content checkout failed. Locale-dependent tests will be skipped."
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/translate-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ jobs:

# Remove the lines that ignore generated content directories and their associated comments
# This preserves all other .gitignore updates from main while allowing commits to these dirs
sed -i '/^# Generated content (synced from content branch)$/d; /^# These directories are populated by checking out from the content branch$/d; /^\/docs\/$/d; /^\/i18n\/$/d; /^\/static\/images\/$/d' .gitignore
# The i18n negation block (theme JSON allowlist for es/pt) must also be stripped here,
# otherwise its /i18n/* rule keeps everything outside navbar.json/footer.json ignored
# on the content branch (e.g. code.json, translated doc pages never get committed).
sed -i '/^# Generated content (synced from content branch)$/d; /^# These directories are populated by checking out from the content branch$/d; /^\/docs\/$/d; /^\/i18n\/$/d; /^\/static\/images\/$/d; /^# Generated i18n content/,/^$/d' .gitignore

# Only commit if .gitignore actually changed
if ! git diff --quiet .gitignore; then
Expand Down
16 changes: 15 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,23 @@ assets/
# Generated content (synced from content branch)
# These directories are populated by checking out from the content branch
/docs/
/i18n/
/static/images/

# Generated i18n content — selectively un-ignore theme JSON for es/pt
/i18n/*
!/i18n/es/
/i18n/es/*
!/i18n/es/docusaurus-theme-classic/
/i18n/es/docusaurus-theme-classic/*
!/i18n/es/docusaurus-theme-classic/navbar.json
!/i18n/es/docusaurus-theme-classic/footer.json
!/i18n/pt/
/i18n/pt/*
!/i18n/pt/docusaurus-theme-classic/
/i18n/pt/docusaurus-theme-classic/*
!/i18n/pt/docusaurus-theme-classic/navbar.json
!/i18n/pt/docusaurus-theme-classic/footer.json
Comment thread
luandro marked this conversation as resolved.

# Generated robots.txt (created at build time based on IS_PRODUCTION env var)
/static/robots.txt

Expand Down
Loading
Loading