Skip to content

Update sync-common.sh #124

Update sync-common.sh

Update sync-common.sh #124

name: ♻️ GitHub Org Sync
on:
push:
branches: [ main, master ]
paths:
# --- Workflow files
- ".github/workflows/github-org-sync.yml"
# --- Heartbeat
- "admin/.sync-heartbeat"
# --- Template folders to watch
- ".github/**"
# --- Source sync files
- "admin/*.sh"
- "admin/*.json"
- "tools/*"
# --- Governance documents
- "CODE_OF_CONDUCT.md"
- "CONTRIBUTING.md"
- "CONTRIBUTING.project.md"
- "LICENSE"
- "NOTICE_PRIVATE.md"
- "SECURITY.md"
# --- Editor files
- ".editorconfig"
- "format-code.ps1"
- "format-code.sh"
workflow_call:
inputs:
max_jobs:
description: "Maximum concurrent repos to sync"
required: false
default: 4
type: number
workflow_dispatch:
inputs:
max_jobs:
description: "Maximum concurrent repos to sync"
required: false
default: 4
type: number
jobs:
# ----------------------------------------
# 🧩 Precheck
# ----------------------------------------
precheck:
# ✅ Only run for org-owned .github repos
if: ${{ github.event.repository.owner.type == 'Organization' && endsWith(github.repository, '/.github') }}
name: 🧮 Precheck
# Org repos → self-hosted, user repos → github-hosted
runs-on: ${{ github.event.repository.owner.type == 'Organization' && 'self-hosted' || 'ubuntu-latest' }}
timeout-minutes: 3
continue-on-error: true
outputs:
# usage.over_limit (org) OR assume-ok.over_limit (user) OR default false
over_limit: ${{ steps.usage.outputs.over_limit || steps.assume_ok.outputs.over_limit || 'false' }}
steps:
- name: 🔎 Owner info (debug)
run: |
echo "Repository: ${{ github.repository }}"
echo "Owner: ${{ github.repository_owner }}"
echo "Owner type: ${{ github.event.repository.owner.type }}"
echo "Runner: $RUNNER_NAME"
echo "Runner OS: $RUNNER_OS"
- name: 🧾 Minimal Checkout for Local Actions
uses: actions/checkout@v4
with:
sparse-checkout: |
.github/actions
.github/scripts
fetch-depth: 1
# Only needed (and only valid) for org repos
- name: ⚙️ Validate GH_TOKEN
if: ${{ github.event.repository.owner.type == 'Organization' }}
uses: ./.github/actions/validate-gh-token
with:
gh-token: ${{ secrets.GH_TOKEN }}
- name: 🧮 Check GitHub Usage (org only)
id: usage
if: ${{ github.event.repository.owner.type == 'Organization' }}
uses: ./.github/actions/check-org-usage
with:
gh-token: ${{ secrets.GH_TOKEN }}
org-name: ${{ github.repository_owner }}
check-actions: "true"
check-packages-bandwidth: "false"
# For user repos, we don't care about org minutes at all:
# just declare over_limit=false and move on.
- name: ✅ Assume under limit (user repos)
id: assume_ok
if: ${{ github.event.repository.owner.type != 'Organization' }}
run: |
echo "This is a user-owned repo; skipping org usage checks."
echo "over_limit=false" >> "$GITHUB_OUTPUT"
- name: 📊 Summary
run: |
echo "Precheck complete."
echo "Owner type: ${{ github.event.repository.owner.type }}"
echo "over_limit = ${{ steps.usage.outputs.over_limit || steps.assume_ok.outputs.over_limit || 'false' }}"
- name: 🧹 Cleanup Runner
if: always()
uses: ./.github/actions/cleanup-runner
# ----------------------------------------
# 🧠 Org Sync
# ----------------------------------------
org-sync:
name: ${{ format('🧠 Org Sync {0}', needs.precheck.outputs.over_limit == 'true' && '(Self-Hosted)' || '(GitHub)') }}
needs: precheck
runs-on: ${{ needs.precheck.outputs.over_limit == 'true' && 'self-hosted' || 'ubuntu-latest' }}
timeout-minutes: 10
steps:
- name: 🏁 Identify Runner
env:
SELF_HOSTED: ${{ needs.precheck.outputs.over_limit }}
run: |
echo "Running Org Sync on $(hostname) (${RUNNER_OS})"
echo "Self Hosted: $SELF_HOSTED"
- name: 🧰 Minimal Checkout for Local Actions
uses: actions/checkout@v4
with:
sparse-checkout: |
.github/actions
.github/scripts
fetch-depth: 1
- name: ⚙️ Configure Git
shell: bash
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global credential.helper store
echo "https://x-access-token:${{ secrets.GH_TOKEN }}@github.com" > ~/.git-credentials
- name: 🧾 Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- name: ⚙️ Install tools
run: |
sudo apt-get update
sudo apt-get install -y git gh jq perl
echo "${{ secrets.GH_TOKEN }}" | gh auth login --with-token
gh auth status -t
- name: 🔮 Run Org Sync
shell: bash
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
MAX_JOBS: ${{ github.event.inputs.max_jobs || 4 }}
run: |
cd admin
set -o pipefail
find . -type f -name "*.sh" -exec sed -i 's/\r$//' {} \;
chmod +x *.sh
start=$(date +%s)
bash sync-core.sh | tee ../sync-output.txt
end=$(date +%s)
echo "⏱️ Sync duration: $(( (end - start) / 60 )) minutes"
- name: 📦 Upload Logs
uses: actions/upload-artifact@v4
if: always()
with:
name: sync-logs
path: admin/logs/
retention-days: 30
- name: 📄 Show Summary
if: always()
shell: bash
run: |
echo "### Sync Completed $(date -u)" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Repository | Status | Standards |" >> "$GITHUB_STEP_SUMMARY"
echo "|-------------|--------|-----------|" >> "$GITHUB_STEP_SUMMARY"
shopt -s nullglob
failed_any=0
total_clobbers=0
for log in admin/logs/*.log; do
repo=$(basename "$log" .log)
# Status detection
if grep -iq "failed" "$log"; then
status="❌ Failed"
failed_any=1
else
status="✅ Success"
fi
# Standards clobber detection (from sync-standards.sh logs)
clobber_count=$(grep -c '\[CLOBBER\]' "$log" || true)
if [[ "$clobber_count" -gt 0 ]]; then
standards="⚠️ ${clobber_count} clobber(s)"
total_clobbers=$(( total_clobbers + clobber_count ))
else
standards="—"
fi
echo "| $repo | $status | $standards |" >> "$GITHUB_STEP_SUMMARY"
done
shopt -u nullglob
if [[ $total_clobbers -gt 0 ]]; then
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "> Standards sync clobbered previously changed paths in this run (**$total_clobbers** total clobber(s) across all repos). See per-repo logs and commits for details." >> "$GITHUB_STEP_SUMMARY"
fi
if [[ $failed_any -ne 0 ]]; then
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "> One or more repositories failed to sync." >> "$GITHUB_STEP_SUMMARY"
# Make the job fail so the workflow shows red
exit 1
fi
- name: 🧹 Cleanup Runner
if: always()
uses: ./.github/actions/cleanup-runner