Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 162 additions & 0 deletions .github/scripts/sync-snapshot.sh

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of this logic is extracted from publish-docs.yaml, so that I could run this locally and manually reason about what will happen

Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#!/usr/bin/env bash
set -euo pipefail

# Decides whether to create or refresh the versioned docs snapshot in the
# downstream embedded-sdk-docs repo, then performs the file operations.
#
# Extracted from publish-docs.yaml so the logic can be exercised locally
# without triggering a full CI sync. In CI the workflow handles cloning,
# wiping, copying fresh source, staging, and pushing; this script only owns
# the snapshot decision and file operations.
#
# Usage:
# .github/scripts/sync-snapshot.sh --downstream <path-to-embedded-sdk-docs>
# [--trigger workflow_run|workflow_dispatch|push]
# [--version <x.y.z>]
# [--dry-run]
#
# --downstream Path to the local embedded-sdk-docs checkout. Required.
# --trigger Simulates the GitHub Actions event name. Defaults to
# workflow_dispatch (equivalent to a manual run: creates a
# snapshot if missing, skips refresh).
# --version Override the SDK version. Defaults to package.json.
# --dry-run Print what would happen without touching any files.
#
# Snapshot action matrix:
# workflow_run, minor missing -> create
# workflow_run, minor present -> refresh
# workflow_dispatch, minor missing -> create
# workflow_dispatch, minor present -> (no-op — manual runs don't refresh)
# push -> (no-op — push path never touches snapshots)
#
# For the create action the script copies docs/ into the downstream's docs/
# and then runs `npx docusaurus docs:version`. Run `npm ci` in the downstream
# docs-site/ first if node_modules is absent.

DOWNSTREAM=""
TRIGGER="workflow_dispatch"
VERSION=""
DRY_RUN=0

while [[ $# -gt 0 ]]; do
case "$1" in
--downstream) DOWNSTREAM="$2"; shift 2 ;;
--trigger) TRIGGER="$2"; shift 2 ;;
--version) VERSION="$2"; shift 2 ;;
--dry-run) DRY_RUN=1; shift ;;
*) echo "Unknown argument: $1" >&2; exit 64 ;;
esac
done

if [[ -z "$DOWNSTREAM" ]]; then
echo "Error: --downstream <path> is required" >&2
exit 64
fi
if [[ ! -d "$DOWNSTREAM" ]]; then
echo "Error: downstream path does not exist: $DOWNSTREAM" >&2
exit 1
fi

# Resolve to the repo root of the SDK (the directory containing docs/ and package.json).
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SDK_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"

if [[ -z "$VERSION" ]]; then
VERSION=$(node -p "require('${SDK_ROOT}/package.json').version")
fi
MINOR=$(echo "$VERSION" | cut -d. -f1,2)

echo "SDK version : ${VERSION} (minor ${MINOR})"
echo "Downstream : ${DOWNSTREAM}"
echo "Trigger : ${TRIGGER}"
[[ "$DRY_RUN" -eq 1 ]] && echo "(dry run — no files will be changed)"
echo ""

# ---- Decide snapshot action ----
MINOR_IN_VERSIONS=false
VERSIONS_JSON="${DOWNSTREAM}/docs-site/versions.json"
if [[ -f "$VERSIONS_JSON" ]] \
&& jq -e --arg key "${MINOR}" 'index($key) != null' "$VERSIONS_JSON" > /dev/null 2>&1; then
MINOR_IN_VERSIONS=true
fi

SNAPSHOT_ACTION=none
if [[ "$TRIGGER" == "workflow_run" ]] || [[ "$TRIGGER" == "workflow_dispatch" ]]; then
if [[ "$MINOR_IN_VERSIONS" == "false" ]]; then
SNAPSHOT_ACTION=create
elif [[ "$TRIGGER" == "workflow_run" ]]; then
SNAPSHOT_ACTION=refresh
fi
fi
echo "Snapshot action: ${SNAPSHOT_ACTION}"
echo ""

if [[ "$SNAPSHOT_ACTION" == "none" ]]; then
echo "Nothing to do."
exit 0
fi

VERSIONED_DIR="${DOWNSTREAM}/docs-site/versioned_docs/version-${MINOR}"

# ---- Create ----
if [[ "$SNAPSHOT_ACTION" == "create" ]]; then
if [[ "$DRY_RUN" -eq 1 ]]; then
echo "Would copy ${SDK_ROOT}/docs/ -> ${DOWNSTREAM}/docs/"
echo "Would run: npx docusaurus docs:version ${MINOR} (in ${DOWNSTREAM}/docs-site)"
else
cp -R "${SDK_ROOT}/docs/." "${DOWNSTREAM}/docs/"
(
cd "${DOWNSTREAM}/docs-site"
[[ ! -d node_modules ]] && npm ci
npx docusaurus docs:version "${MINOR}"
)
echo "Created snapshot version-${MINOR}."
fi
exit 0
fi

# ---- Refresh ----
if [[ ! -d "$VERSIONED_DIR" ]]; then
echo "Error: ${MINOR} is in versions.json but ${VERSIONED_DIR}/ is missing." >&2
exit 1
fi

updated=0
orphan=0
added=0

# Update files already in the snapshot.
while IFS= read -r snap_file; do
rel="${snap_file#${VERSIONED_DIR}/}"
live="${SDK_ROOT}/docs/${rel}"
if [[ -f "$live" ]]; then
[[ "$DRY_RUN" -eq 0 ]] && cp "$live" "$snap_file"
updated=$((updated + 1))
else
echo " orphan (no live counterpart): ${rel}"
orphan=$((orphan + 1))
fi
done < <(find "$VERSIONED_DIR" -type f | sort)

# Add files present in live docs/ but absent from the snapshot.
while IFS= read -r live_file; do
rel="${live_file#${SDK_ROOT}/docs/}"
snap="${VERSIONED_DIR}/${rel}"
if [[ ! -f "$snap" ]]; then
echo " add (missing from snapshot): ${rel}"
if [[ "$DRY_RUN" -eq 0 ]]; then
mkdir -p "$(dirname "$snap")"
cp "$live_file" "$snap"
fi
added=$((added + 1))
fi
done < <(find "${SDK_ROOT}/docs" -type f | sort)

echo ""
echo "Refreshed ${updated} file(s) in version-${MINOR}; ${added} added; ${orphan} orphan(s) left as-is."

if [[ "${added}" -gt 0 ]] || [[ "${orphan}" -gt 0 ]]; then
echo ""
echo "WARNING: structural changes detected — ${added} file(s) added, ${orphan} orphan(s)."
echo "Verify that version-${MINOR} snapshot looks correct before merging."
fi
53 changes: 15 additions & 38 deletions .github/workflows/publish-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ name: Sync Docs Source
#
# Patch refresh: walks versioned_docs/version-<X.Y>/ and refreshes the
# content of each file from the matching path in the freshly-synced docs/.
# Files in the snapshot that have no live counterpart are left as-is.
# Files in live docs/ that aren't already in the snapshot are NOT added —
# adding a doc to a released minor is an intentional change made directly
# in the downstream repo.
# Files in live docs/ that are absent from the snapshot are added — a patch
# may rename or add files, and without them updated cross-links break the
# build. Files in the snapshot with no live counterpart (orphans from a
# rename or deletion) are left as-is so existing external links keep
# resolving. If any structural changes occur (additions or orphans), a
# Slack notification fires so someone can verify the snapshot looks right.
#
# Auth: a GitHub App installed on Gusto/embedded-sdk-docs with
# Contents: write. Credentials live in this repo's `publish-docs`
Expand Down Expand Up @@ -194,18 +196,13 @@ jobs:
done

# ---- Step 4: snapshot creation or refresh ----
# Snapshot ACTION is determined by trigger + downstream state:
# workflow_run, minor missing -> create (new minor)
# workflow_run, minor present -> refresh (patch)
# workflow_dispatch, missing -> create (bootstrap / recover)
# workflow_dispatch, present -> none
# push -> none
# Determine the action before running the script so we have it
# for the commit message label regardless of what the script changes.
MINOR_IN_VERSIONS=false
if [ -f docs-site/versions.json ] \
&& jq -e --arg key "${MINOR}" 'index($key) != null' docs-site/versions.json > /dev/null 2>&1; then
MINOR_IN_VERSIONS=true
fi

SNAPSHOT_ACTION=none
if [ "$TRIGGER" = "workflow_run" ] || [ "$TRIGGER" = "workflow_dispatch" ]; then
if [ "$MINOR_IN_VERSIONS" = "false" ]; then
Expand All @@ -214,34 +211,14 @@ jobs:
SNAPSHOT_ACTION=refresh
fi
fi
echo "Snapshot action for SDK ${SDK_VERSION} (minor ${MINOR}, trigger ${TRIGGER}): ${SNAPSHOT_ACTION}"

if [ "$SNAPSHOT_ACTION" = "create" ]; then
# docusaurus docs:version copies docs/ -> versioned_docs/version-<minor>/,
# captures the sidebar, prepends <minor> to versions.json.
# The conditional config picks up versions.json[0] as lastVersion automatically.
(cd docs-site && npm ci && npx docusaurus docs:version "${MINOR}")
echo "Created snapshot version-${MINOR} in downstream."
elif [ "$SNAPSHOT_ACTION" = "refresh" ]; then
VERSIONED_DIR="docs-site/versioned_docs/version-${MINOR}"
if [ ! -d "$VERSIONED_DIR" ]; then
echo "::error::${MINOR} is in versions.json but ${VERSIONED_DIR}/ is missing in downstream."
exit 1
fi
updated=0
orphan=0
while IFS= read -r snap_file; do
rel="${snap_file#${VERSIONED_DIR}/}"
live="docs/${rel}"
if [ -f "$live" ]; then
cp "$live" "$snap_file"
updated=$((updated + 1))
else
orphan=$((orphan + 1))
fi
done < <(find "$VERSIONED_DIR" -type f)
echo "Refreshed ${updated} file(s) in version-${MINOR}; ${orphan} orphan(s) left as-is."
fi
# Delegate to the script so this logic can also be run locally.
# In CI $(pwd) is the cloned downstream working tree (docs-repo/).
# The create path runs npm ci inside the downstream docs-site/.
"${GITHUB_WORKSPACE}/.github/scripts/sync-snapshot.sh" \
--downstream "$(pwd)" \
--trigger "${TRIGGER}" \
--version "${SDK_VERSION}"

# ---- Step 5: stage and validate ----
# Stage only the paths we manage. NOT `git add -A` — that could
Expand Down
Loading