Skip to content

Commit 263549d

Browse files
committed
refactor(sync): consolidate to sync.ts as single source of truth
- sync.ts now owns all file lists (SYNC_SCRIPTS, SYNC_COMMANDS, SYNC_RULES) - sync.sh slimmed to a thin bootstrap wrapper that delegates to sync.ts - sync-all.sh calls npx tsx sync.ts directly instead of bash sync.sh - Added missing asvs-check.ts entry to sync.ts (was only in sync.sh) Eliminates duplicate file lists that kept drifting out of sync.
1 parent b936865 commit 263549d

3 files changed

Lines changed: 29 additions & 316 deletions

File tree

scripts/sync-all.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ fi
6161

6262
echo ""
6363
echo -e "${BOLD}Syncing ${#REPOS[@]} repos from esolia.github${NC}"
64-
echo -e " Source: ${BLUE}$ESOLIA_GITHUB_ROOT/scripts/sync.sh${NC}"
64+
echo -e " Source: ${BLUE}$ESOLIA_GITHUB_ROOT/scripts/sync.ts${NC}"
6565
echo -e " Repos: ${BLUE}$PARENT_DIR${NC}"
6666
echo ""
6767

@@ -78,7 +78,7 @@ for repo in "${REPOS[@]}"; do
7878
continue
7979
fi
8080

81-
if cd "$REPO_DIR" && bash "$ESOLIA_GITHUB_ROOT/scripts/sync.sh" $CHECK_FLAG > /dev/null 2>&1; then
81+
if cd "$REPO_DIR" && npx tsx "$ESOLIA_GITHUB_ROOT/scripts/sync.ts" $CHECK_FLAG > /dev/null 2>&1; then
8282
if [[ -n "$CHECK_FLAG" ]]; then
8383
echo -e "${GREEN}$repo${NC} — up-to-date"
8484
else

scripts/sync.sh

Lines changed: 19 additions & 311 deletions
Original file line numberDiff line numberDiff line change
@@ -1,337 +1,45 @@
11
#!/bin/bash
2-
# sync.sh - Fetch centralized scripts, commands, and rules from esolia.github
2+
# sync.sh — Bootstrap wrapper for sync.ts
3+
#
4+
# This is a thin wrapper. The real logic and file lists live in sync.ts.
35
#
46
# Usage:
57
# curl -sSfL https://raw.githubusercontent.com/eSolia/.github/main/scripts/sync.sh | bash
6-
# ./scripts/shared/sync.sh # Re-sync (after initial fetch)
7-
# ./scripts/shared/sync.sh --check # Check if scripts are up-to-date
8-
# ./scripts/shared/sync.sh --ref v1.0.0 # Pin to a specific tag/SHA
9-
# ./scripts/shared/sync.sh --scripts-only # Only sync scripts (skip commands/rules)
10-
#
11-
# Downloads centralized scripts into scripts/shared/, shared Claude commands
12-
# into .claude/commands/, and shared rules into .claude/rules/.
8+
# ./scripts/shared/sync.sh # Re-sync (delegates to sync.ts)
9+
# ./scripts/shared/sync.sh --check # Check for updates
10+
# ./scripts/shared/sync.sh --ref v1.0.0 # Pin to a specific tag/SHA
1311

1412
set -e
1513

1614
# ════════════════════════════════════════════════════════════════════════════
17-
# Configuration
18-
# ════════════════════════════════════════════════════════════════════════════
19-
20-
REPO_OWNER="eSolia"
21-
REPO_NAME=".github"
22-
DEFAULT_REF="main"
23-
REF="$DEFAULT_REF"
24-
CHECK_ONLY=false
25-
SCRIPTS_ONLY=false
26-
27-
# Scripts to sync (source path in esolia.github -> local path under scripts/shared/)
28-
SYNC_SCRIPTS=(
29-
"scripts/lib/common.sh:lib/common.sh"
30-
"scripts/bump-version.sh:bump-version.sh"
31-
"scripts/update-wrangler.sh:update-wrangler.sh"
32-
"scripts/audit-backpressure.sh:audit-backpressure.sh"
33-
"scripts/asvs-check.ts:asvs-check.ts"
34-
"scripts/sync.sh:sync.sh"
35-
"scripts/sync.ts:sync.ts"
36-
"scripts/submit-bing.mts:submit-bing.mts"
37-
"scripts/cross-post-devto.mts:cross-post-devto.mts"
38-
"scripts/cross-post-qiita.mts:cross-post-qiita.mts"
39-
)
40-
41-
# Shared commands to sync (source -> .claude/commands/)
42-
SYNC_COMMANDS=(
43-
".claude/shared-commands/backpressure-review.md:backpressure-review.md"
44-
".claude/shared-commands/seo-setup.md:seo-setup.md"
45-
".claude/shared-commands/seo-report.md:seo-report.md"
46-
".claude/shared-commands/checkpoint.md:checkpoint.md"
47-
".claude/shared-commands/commit-style.md:commit-style.md"
48-
".claude/shared-commands/dev/d1-health.md:dev/d1-health.md"
49-
".claude/shared-commands/dev/preflight.md:dev/preflight.md"
50-
".claude/shared-commands/dev/svelte-review.md:dev/svelte-review.md"
51-
".claude/shared-commands/security/audit-github-actions.md:security/audit-github-actions.md"
52-
".claude/shared-commands/security/harden-github-org.md:security/harden-github-org.md"
53-
".claude/shared-commands/standards/check.md:standards/check.md"
54-
".claude/shared-commands/standards/list.md:standards/list.md"
55-
".claude/shared-commands/standards/search.md:standards/search.md"
56-
".claude/shared-commands/standards/writing.md:standards/writing.md"
57-
)
58-
59-
# Shared rules to sync (source -> .claude/rules/)
60-
SYNC_RULES=(
61-
".claude/shared-rules/backpressure-verify.md:backpressure-verify.md"
62-
".claude/shared-rules/d1-maintenance.md:d1-maintenance.md"
63-
".claude/shared-rules/mermaid-diagrams.md:mermaid-diagrams.md"
64-
)
65-
66-
# Colors (inline — can't source common.sh before it's downloaded)
67-
RED='\033[0;31m'
68-
GREEN='\033[0;32m'
69-
YELLOW='\033[1;33m'
70-
BLUE='\033[0;34m'
71-
CYAN='\033[0;36m'
72-
BOLD='\033[1m'
73-
NC='\033[0m'
74-
75-
print_step() { echo -e "${BLUE}==>${NC} $1"; }
76-
print_success() { echo -e "${GREEN}${NC} $1"; }
77-
print_warning() { echo -e "${YELLOW}${NC} $1"; }
78-
print_error() { echo -e "${RED}${NC} $1"; }
79-
print_info() { echo -e "${CYAN}${NC} $1"; }
80-
81-
# ════════════════════════════════════════════════════════════════════════════
82-
# Parse Arguments
83-
# ════════════════════════════════════════════════════════════════════════════
84-
85-
while [[ $# -gt 0 ]]; do
86-
case "$1" in
87-
--ref) REF="$2"; shift 2 ;;
88-
--check) CHECK_ONLY=true; shift ;;
89-
--scripts-only) SCRIPTS_ONLY=true; shift ;;
90-
--help|-h)
91-
echo "Usage: $0 [--ref <tag-or-sha>] [--check] [--scripts-only]"
92-
echo ""
93-
echo "Options:"
94-
echo " --ref <ref> Git ref to fetch from (default: main)"
95-
echo " --check Check if local scripts match remote (exit 1 if stale)"
96-
echo " --scripts-only Only sync scripts (skip commands and rules)"
97-
echo " --help Show this help"
98-
exit 0
99-
;;
100-
*)
101-
print_error "Unknown option: $1"
102-
exit 1
103-
;;
104-
esac
105-
done
106-
107-
# ════════════════════════════════════════════════════════════════════════════
108-
# Find project root
15+
# Find project root and shared directory
10916
# ════════════════════════════════════════════════════════════════════════════
11017

11118
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
11219
SHARED_DIR="$PROJECT_ROOT/scripts/shared"
113-
SCRIPTVERSION_FILE="$SHARED_DIR/.scriptversion"
114-
115-
# ════════════════════════════════════════════════════════════════════════════
116-
# Check mode
117-
# ════════════════════════════════════════════════════════════════════════════
118-
119-
if [ "$CHECK_ONLY" = true ]; then
120-
if [ ! -f "$SCRIPTVERSION_FILE" ]; then
121-
print_error "No .scriptversion found — scripts have never been synced"
122-
exit 1
123-
fi
124-
125-
LOCAL_COMMIT=$(grep '^commit=' "$SCRIPTVERSION_FILE" 2>/dev/null | cut -d= -f2)
126-
LOCAL_FETCHED=$(grep '^fetched=' "$SCRIPTVERSION_FILE" 2>/dev/null | cut -d= -f2)
127-
LOCAL_REF=$(grep '^ref=' "$SCRIPTVERSION_FILE" 2>/dev/null | cut -d= -f2)
128-
129-
print_info "Local: commit=$LOCAL_COMMIT ref=$LOCAL_REF fetched=$LOCAL_FETCHED"
130-
131-
# Fetch remote HEAD for the ref
132-
REMOTE_COMMIT=$(git ls-remote "https://github.com/$REPO_OWNER/$REPO_NAME.git" "$LOCAL_REF" 2>/dev/null | head -1 | cut -f1)
133-
134-
if [ -z "$REMOTE_COMMIT" ]; then
135-
print_warning "Could not fetch remote HEAD (network issue?)"
136-
exit 1
137-
fi
138-
139-
print_info "Remote: commit=$REMOTE_COMMIT ref=$LOCAL_REF"
140-
141-
if [ "$LOCAL_COMMIT" = "$REMOTE_COMMIT" ]; then
142-
print_success "Scripts are up-to-date"
143-
exit 0
144-
else
145-
print_warning "Scripts are stale — run sync.sh to update"
146-
exit 1
147-
fi
148-
fi
20+
SYNC_TS="$SHARED_DIR/sync.ts"
14921

15022
# ════════════════════════════════════════════════════════════════════════════
151-
# Sync mode
23+
# Bootstrap: download sync.ts if it doesn't exist yet
15224
# ════════════════════════════════════════════════════════════════════════════
15325

154-
echo ""
155-
echo -e "${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}"
156-
echo -e "${BOLD}║ Sync from eSolia/.github ║${NC}"
157-
echo -e "${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}"
158-
echo ""
159-
print_info "Source: $REPO_OWNER/$REPO_NAME@$REF"
160-
print_info "Target: $PROJECT_ROOT"
161-
echo ""
162-
163-
BASE_URL="https://raw.githubusercontent.com/$REPO_OWNER/$REPO_NAME/$REF"
164-
165-
# Helper: download a list of files
166-
download_files() {
167-
local list_name=$1
168-
local target_dir="$2"
169-
local label="$3"
170-
171-
eval "local entries=(\"\${${list_name}[@]}\")"
172-
for entry in "${entries[@]}"; do
173-
src="${entry%%:*}"
174-
dest="${entry##*:}"
175-
url="$BASE_URL/$src"
176-
target="$target_dir/$dest"
177-
178-
# Ensure parent directory exists
179-
mkdir -p "$(dirname "$target")"
180-
181-
if curl -sSfL "$url" -o "$target" 2>/dev/null; then
182-
# Make .sh files executable
183-
[[ "$target" == *.sh ]] && chmod +x "$target"
184-
print_success "$dest"
185-
else
186-
print_error "Failed to download $src"
187-
exit 1
188-
fi
189-
done
190-
}
191-
192-
# ── 1. Sync scripts ──
193-
mkdir -p "$SHARED_DIR/lib"
194-
print_step "Downloading scripts"
195-
download_files SYNC_SCRIPTS "$SHARED_DIR" "scripts"
196-
echo ""
197-
198-
# ── 2. Sync commands and rules (unless --scripts-only) ──
199-
if [ "$SCRIPTS_ONLY" = false ]; then
200-
COMMANDS_DIR="$PROJECT_ROOT/.claude/commands"
201-
RULES_DIR="$PROJECT_ROOT/.claude/rules"
202-
203-
if [ ${#SYNC_COMMANDS[@]} -gt 0 ]; then
204-
mkdir -p "$COMMANDS_DIR"
205-
print_step "Syncing shared commands to .claude/commands/"
206-
download_files SYNC_COMMANDS "$COMMANDS_DIR" "commands"
207-
echo ""
208-
fi
209-
210-
if [ ${#SYNC_RULES[@]} -gt 0 ]; then
211-
mkdir -p "$RULES_DIR"
212-
print_step "Syncing shared rules to .claude/rules/"
213-
download_files SYNC_RULES "$RULES_DIR" "rules"
214-
echo ""
215-
fi
26+
if [ ! -f "$SYNC_TS" ]; then
27+
echo "Bootstrapping: downloading sync.ts from eSolia/.github ..."
28+
mkdir -p "$SHARED_DIR"
29+
curl -sSfL "https://raw.githubusercontent.com/eSolia/.github/main/scripts/sync.ts" -o "$SYNC_TS"
21630
fi
21731

21832
# ════════════════════════════════════════════════════════════════════════════
219-
# Write .scriptversion
220-
# ════════════════════════════════════════════════════════════════════════════
221-
222-
REMOTE_COMMIT=$(git ls-remote "https://github.com/$REPO_OWNER/$REPO_NAME.git" "$REF" 2>/dev/null | head -1 | cut -f1)
223-
REMOTE_COMMIT="${REMOTE_COMMIT:-unknown}"
224-
FETCH_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
225-
226-
cat > "$SCRIPTVERSION_FILE" << EOF
227-
commit=$REMOTE_COMMIT
228-
fetched=$FETCH_TIME
229-
ref=$REF
230-
EOF
231-
232-
print_success "Wrote .scriptversion (commit=${REMOTE_COMMIT:0:12})"
233-
echo ""
234-
235-
# ════════════════════════════════════════════════════════════════════════════
236-
# Create thin wrappers (only if they don't already exist)
33+
# Require Node.js
23734
# ════════════════════════════════════════════════════════════════════════════
23835

239-
SCRIPTS_DIR="$PROJECT_ROOT/scripts"
240-
241-
create_wrapper() {
242-
local name="$1"
243-
local wrapper="$SCRIPTS_DIR/$name"
244-
245-
if [ -f "$wrapper" ]; then
246-
# Check if it's already a wrapper (contains "shared/" reference)
247-
if grep -q 'shared/' "$wrapper" 2>/dev/null; then
248-
print_info "$name wrapper already exists"
249-
return
250-
fi
251-
# It's a local script — back it up
252-
local backup="${wrapper}.local-backup"
253-
cp "$wrapper" "$backup"
254-
print_warning "Backed up existing $name to ${name}.local-backup"
255-
fi
256-
257-
cat > "$wrapper" << WRAPPER
258-
#!/bin/bash
259-
# Wrapper — delegates to centralized script from esolia.github
260-
# To update: ./scripts/shared/sync.sh
261-
SCRIPT_DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
262-
exec "\$SCRIPT_DIR/shared/$name" "\$@"
263-
WRAPPER
264-
265-
chmod +x "$wrapper"
266-
print_success "Created wrapper: scripts/$name"
267-
}
268-
269-
print_step "Setting up wrappers"
270-
create_wrapper "bump-version.sh"
271-
create_wrapper "update-wrangler.sh"
272-
create_wrapper "audit-backpressure.sh"
273-
echo ""
274-
275-
# ════════════════════════════════════════════════════════════════════════════
276-
# Add shared dir to .gitignore if not already there
277-
# ════════════════════════════════════════════════════════════════════════════
278-
279-
GITIGNORE="$PROJECT_ROOT/.gitignore"
280-
if [ -f "$GITIGNORE" ]; then
281-
if ! grep -qF 'scripts/shared/' "$GITIGNORE" 2>/dev/null; then
282-
print_step "Adding scripts/shared/ to .gitignore"
283-
echo "" >> "$GITIGNORE"
284-
echo "# Centralized scripts fetched from esolia.github" >> "$GITIGNORE"
285-
echo "scripts/shared/" >> "$GITIGNORE"
286-
print_success "Updated .gitignore"
287-
echo ""
288-
fi
289-
else
290-
print_info "No .gitignore found — consider adding scripts/shared/ to it"
291-
echo ""
36+
if ! command -v node &>/dev/null; then
37+
echo "Error: Node.js is required. Install it from https://nodejs.org/" >&2
38+
exit 1
29239
fi
29340

29441
# ════════════════════════════════════════════════════════════════════════════
295-
# Summary
42+
# Delegate to sync.ts
29643
# ════════════════════════════════════════════════════════════════════════════
29744

298-
echo -e "${BOLD}── Sync Complete ──────────────────────────────────────────────${NC}"
299-
echo ""
300-
echo " Synced from: $REPO_OWNER/$REPO_NAME@$REF"
301-
echo " Commit: ${REMOTE_COMMIT:0:12}"
302-
echo ""
303-
echo " Scripts:"
304-
echo " ./scripts/bump-version.sh <version> # Bump version + QC"
305-
echo " ./scripts/bump-version.sh --qc-only # QC checks only"
306-
echo " ./scripts/update-wrangler.sh # Update wrangler"
307-
echo " ./scripts/audit-backpressure.sh # Backpressure audit"
308-
echo " npx tsx scripts/shared/asvs-check.ts # ASVS compliance check"
309-
echo " npx tsx scripts/shared/submit-bing.mts # Bing URL submission"
310-
if [ "$SCRIPTS_ONLY" = false ]; then
311-
echo ""
312-
echo " Commands (in .claude/commands/):"
313-
echo " /backpressure-review # SvelteKit quality review"
314-
echo " /seo-setup # SEO checklist + setup"
315-
echo " /checkpoint # Save session checkpoint"
316-
echo " /commit-style # Conventional commit reference"
317-
echo " /dev:d1-health # D1 database health audit"
318-
echo " /dev:preflight # Show preflight checks"
319-
echo " /dev:svelte-review # Svelte 5 best practices review"
320-
echo " /security:audit-github-actions # GitHub Actions security audit"
321-
echo " /security:harden-github-org # GitHub org hardening"
322-
echo " /standards:check # Review code against standards"
323-
echo " /standards:list # List all eSolia standards"
324-
echo " /standards:search # Search standards by keyword"
325-
echo " /standards:writing # Review content against writing guides"
326-
echo ""
327-
echo " Rules (in .claude/rules/):"
328-
echo " backpressure-verify # Auto-verify after code changes"
329-
echo " d1-maintenance # D1 database best practices"
330-
echo " mermaid-diagrams # Compact diagram styling"
331-
fi
332-
echo ""
333-
echo " Maintenance:"
334-
echo " ./scripts/shared/sync.sh # Re-sync (bash)"
335-
echo " npx tsx scripts/shared/sync.ts # Re-sync (cross-platform)"
336-
echo " ./scripts/shared/sync.sh --check # Check for updates"
337-
echo ""
45+
exec npx tsx "$SYNC_TS" "$@"

0 commit comments

Comments
 (0)