From cbb321bec6a6ca0a80f1159185e121f67401c13c Mon Sep 17 00:00:00 2001 From: AlisonWonderland Date: Sun, 29 Mar 2026 22:06:55 +1100 Subject: [PATCH 1/2] =?UTF-8?q?fix(workflow):=20repair=20sync-large-assets?= =?UTF-8?q?=20=E2=80=94=20heredoc-in-YAML=20parse=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflow has never successfully run since it was added. Root cause: bash heredocs (< --- .github/workflows/sync-large-assets.yml | 159 +++++++++++++----------- 1 file changed, 89 insertions(+), 70 deletions(-) diff --git a/.github/workflows/sync-large-assets.yml b/.github/workflows/sync-large-assets.yml index 02c3921c8..54b881a25 100644 --- a/.github/workflows/sync-large-assets.yml +++ b/.github/workflows/sync-large-assets.yml @@ -1,9 +1,15 @@ -name: Sync Large Assets +name: "Sync Large Assets" on: push: branches: - docs-v2 + - docs-v2-dev + paths: + - "snippets/assets/**" + - "v2/assets/**" + schedule: + - cron: "0 0 * * 0" workflow_dispatch: inputs: target_branch: @@ -11,9 +17,9 @@ on: required: false default: "docs-v2-assets" threshold_mb: - description: "File size threshold in MB" + description: "File size threshold in MB (default 1)" required: false - default: "20" + default: "1" permissions: contents: write @@ -22,48 +28,59 @@ jobs: sync-large-assets: runs-on: ubuntu-latest env: - DEFAULT_TARGET_BRANCH: docs-v2-assets - DEFAULT_THRESHOLD_MB: "20" + TARGET_BRANCH: "docs-v2-assets" + THRESHOLD_MB: "1" steps: - name: Checkout source branch uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Resolve inputs + id: config + run: | + if [ -n "${{ inputs.target_branch }}" ]; then + echo "target=${{ inputs.target_branch }}" >> "$GITHUB_OUTPUT" + else + echo "target=${{ env.TARGET_BRANCH }}" >> "$GITHUB_OUTPUT" + fi + if [ -n "${{ inputs.threshold_mb }}" ]; then + echo "threshold=${{ inputs.threshold_mb }}" >> "$GITHUB_OUTPUT" + else + echo "threshold=${{ env.THRESHOLD_MB }}" >> "$GITHUB_OUTPUT" + fi + - name: Sync large assets to target branch shell: bash + env: + RESOLVED_TARGET: ${{ steps.config.outputs.target }} + RESOLVED_THRESHOLD: ${{ steps.config.outputs.threshold }} run: | set -euo pipefail SOURCE_SHA="${GITHUB_SHA}" SOURCE_REF="${GITHUB_REF_NAME}" - - TARGET_BRANCH="${{ github.event.inputs.target_branch || '' }}" - if [ -z "$TARGET_BRANCH" ]; then - TARGET_BRANCH="$DEFAULT_TARGET_BRANCH" - fi - - THRESHOLD_MB="${{ github.event.inputs.threshold_mb || '' }}" - if [ -z "$THRESHOLD_MB" ]; then - THRESHOLD_MB="$DEFAULT_THRESHOLD_MB" - fi + TARGET_BRANCH="${RESOLVED_TARGET}" + THRESHOLD_MB="${RESOLVED_THRESHOLD}" if ! [[ "$THRESHOLD_MB" =~ ^[0-9]+$ ]]; then - echo "threshold_mb must be an integer" + echo "::error::threshold_mb must be an integer, got: $THRESHOLD_MB" exit 1 fi THRESHOLD_BYTES=$((THRESHOLD_MB * 1024 * 1024)) + echo "::group::Configuration" echo "Source branch: $SOURCE_REF" - echo "Source SHA: $SOURCE_SHA" + echo "Source SHA: $SOURCE_SHA" echo "Target branch: $TARGET_BRANCH" - echo "Threshold: ${THRESHOLD_MB}MB (${THRESHOLD_BYTES} bytes)" + echo "Threshold: ${THRESHOLD_MB} MB (${THRESHOLD_BYTES} bytes)" + echo "::endgroup::" git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git fetch origin "$TARGET_BRANCH" || true + git fetch origin "$TARGET_BRANCH" 2>/dev/null || true WORKTREE_DIR="../_assets-branch" rm -rf "$WORKTREE_DIR" @@ -71,73 +88,75 @@ jobs: if git show-ref --verify --quiet "refs/remotes/origin/$TARGET_BRANCH"; then git worktree add -B "$TARGET_BRANCH" "$WORKTREE_DIR" "origin/$TARGET_BRANCH" else + echo "Target branch $TARGET_BRANCH does not exist — creating it." git worktree add -b "$TARGET_BRANCH" "$WORKTREE_DIR" - ( - cd "$WORKTREE_DIR" - : > .nojekyll - mkdir -p .github - cat > .github/README.md <<'EOF' -This branch is auto-managed by .github/workflows/sync-large-assets.yml. -It stores large asset files mirrored from docs-v2 for static hosting. -EOF - git add .nojekyll .github/README.md - git commit -m "Initialize large-assets branch" - git push -u origin "$TARGET_BRANCH" - ) + pushd "$WORKTREE_DIR" > /dev/null + printf '' > .nojekyll + mkdir -p .github + echo "Auto-managed by sync-large-assets.yml. Stores large asset files for static hosting." > .github/README.md + git add .nojekyll .github/README.md + git commit -m "chore: initialise large-assets branch" + git push -u origin "$TARGET_BRANCH" + popd > /dev/null fi - LARGE_LIST_FILE="$(mktemp)" + # Build list of files exceeding threshold + LARGE_LIST="$(mktemp)" + file_count=0 while IFS= read -r file; do [ -z "$file" ] && continue size=$(wc -c < "$file") if [ "$size" -gt "$THRESHOLD_BYTES" ]; then - echo "$file" >> "$LARGE_LIST_FILE" + echo "$file" >> "$LARGE_LIST" + file_count=$((file_count + 1)) fi done < <(git ls-files 'snippets/assets/**' 'v2/assets/**') - sort -u "$LARGE_LIST_FILE" -o "$LARGE_LIST_FILE" + sort -u "$LARGE_LIST" -o "$LARGE_LIST" - echo "Large assets selected:" - if [ -s "$LARGE_LIST_FILE" ]; then - cat "$LARGE_LIST_FILE" + echo "::group::Large assets selected ($file_count files)" + if [ -s "$LARGE_LIST" ]; then + cat "$LARGE_LIST" else - echo "(none)" + echo "(none — no files exceed ${THRESHOLD_MB} MB)" fi + echo "::endgroup::" - ( - cd "$WORKTREE_DIR" + # Sync to target branch + pushd "$WORKTREE_DIR" > /dev/null - # Remove previously tracked mirrored assets not present in latest selection. - while IFS= read -r tracked; do - [ -z "$tracked" ] && continue - if ! grep -Fxq "$tracked" "$LARGE_LIST_FILE"; then - rm -f "$tracked" - fi - done < <(git ls-files 'snippets/assets/**' 'v2/assets/**') + # Remove previously tracked assets no longer in the selection + while IFS= read -r tracked; do + [ -z "$tracked" ] && continue + if ! grep -Fxq "$tracked" "$LARGE_LIST"; then + rm -f "$tracked" + fi + done < <(git ls-files 'snippets/assets/**' 'v2/assets/**') - # Copy current large assets from source checkout. - while IFS= read -r file; do - [ -z "$file" ] && continue - mkdir -p "$(dirname "$file")" - cp -f "${GITHUB_WORKSPACE}/$file" "$file" - done < "$LARGE_LIST_FILE" + # Copy selected large assets from source checkout + while IFS= read -r file; do + [ -z "$file" ] && continue + mkdir -p "$(dirname "$file")" + cp -f "${GITHUB_WORKSPACE}/$file" "$file" + done < "$LARGE_LIST" + + # Write manifest + mkdir -p .github + printf 'source_branch=%s\nsource_sha=%s\nthreshold_mb=%s\nfile_count=%s\ngenerated_at=%s\n' \ + "$SOURCE_REF" "$SOURCE_SHA" "$THRESHOLD_MB" "$file_count" \ + "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \ + > .github/assets-manifest.txt + + git add -A snippets/assets v2/assets .nojekyll .github/assets-manifest.txt .github/README.md + + if git diff --cached --quiet; then + echo "No large-asset changes to commit." + exit 0 + fi - mkdir -p .github - cat > .github/assets-manifest.txt < /dev/null From 355eba9cca77d61ca700df59c1cdfeebed129039 Mon Sep 17 00:00:00 2001 From: AlisonWonderland Date: Sun, 29 Mar 2026 22:10:55 +1100 Subject: [PATCH 2/2] feat(workflow): add dry_run input to sync-large-assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a boolean dry_run input to workflow_dispatch. When enabled, the workflow runs all selection and staging logic but skips the push — reports what would be synced via --stat output instead. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/sync-large-assets.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/sync-large-assets.yml b/.github/workflows/sync-large-assets.yml index 54b881a25..1a3566c43 100644 --- a/.github/workflows/sync-large-assets.yml +++ b/.github/workflows/sync-large-assets.yml @@ -20,6 +20,11 @@ on: description: "File size threshold in MB (default 1)" required: false default: "1" + dry_run: + description: "Dry run — report what would be synced without pushing" + type: boolean + required: false + default: false permissions: contents: write @@ -49,12 +54,14 @@ jobs: else echo "threshold=${{ env.THRESHOLD_MB }}" >> "$GITHUB_OUTPUT" fi + echo "dry_run=${{ inputs.dry_run || 'false' }}" >> "$GITHUB_OUTPUT" - name: Sync large assets to target branch shell: bash env: RESOLVED_TARGET: ${{ steps.config.outputs.target }} RESOLVED_THRESHOLD: ${{ steps.config.outputs.threshold }} + DRY_RUN: ${{ steps.config.outputs.dry_run }} run: | set -euo pipefail @@ -155,6 +162,14 @@ jobs: exit 0 fi + if [ "$DRY_RUN" = "true" ]; then + echo "::notice::DRY RUN — would sync $file_count assets to $TARGET_BRANCH" + echo "::group::Files that would be committed" + git diff --cached --stat + echo "::endgroup::" + exit 0 + fi + git commit -m "chore(assets): sync from ${SOURCE_REF}@${SOURCE_SHA:0:7}" git push origin "$TARGET_BRANCH" echo "::notice::Synced $file_count assets to $TARGET_BRANCH"