Sync Best Practices #24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Sync Best Practices | |
| on: | |
| schedule: | |
| - cron: '0 10 * * 1-5' # Weekdays at 10am UTC (midi Paris) | |
| workflow_dispatch: {} # Manual trigger | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| check-release: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| outputs: | |
| new_release: ${{ steps.check.outputs.new_release }} | |
| latest_version: ${{ steps.check.outputs.latest_version }} | |
| stored_version: ${{ steps.check.outputs.stored_version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check for new Claude Code release | |
| id: check | |
| run: | | |
| LATEST=$(npm view @anthropic-ai/claude-code version 2>/dev/null || echo "unknown") | |
| STORED=$(grep -oP 'Latest version: v\K[0-9.]+' docs/claude-cookbook-best-practices.md || echo "0.0.0") | |
| echo "latest_version=$LATEST" >> "$GITHUB_OUTPUT" | |
| echo "stored_version=$STORED" >> "$GITHUB_OUTPUT" | |
| if [ "$LATEST" = "$STORED" ]; then | |
| echo "new_release=false" >> "$GITHUB_OUTPUT" | |
| echo "No new release (current: v$STORED)" | |
| else | |
| echo "new_release=true" >> "$GITHUB_OUTPUT" | |
| echo "New release detected: v$STORED -> v$LATEST" | |
| fi | |
| - name: Summary | |
| if: steps.check.outputs.new_release == 'false' | |
| run: | | |
| { | |
| echo "## Sync — $(date +%Y-%m-%d)" | |
| echo "" | |
| echo "**Statut** : Pas de nouvelle release (v${{ steps.check.outputs.stored_version }}). Skipped." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| sync: | |
| needs: check-release | |
| if: needs.check-release.outputs.new_release == 'true' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Install Claude Code | |
| run: npm install -g @anthropic-ai/claude-code | |
| - name: Setup workspace | |
| run: | | |
| # Symlink checkout to ~/.claude so commands resolve correctly | |
| ln -sfn "$GITHUB_WORKSPACE" "$HOME/.claude" | |
| # Git identity for commits | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Sync best practices | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| PREV="${{ needs.check-release.outputs.stored_version }}" | |
| NEXT="${{ needs.check-release.outputs.latest_version }}" | |
| claude -p "/sync-cookbook — New release detected: v${PREV} -> v${NEXT}. Update the version header to v${NEXT} and focus changelog fetch on versions after v${PREV}." \ | |
| --model claude-sonnet-4-6 \ | |
| --dangerously-skip-permissions \ | |
| --max-turns 30 \ | |
| --output-format text \ | |
| | tee /tmp/sync-report.txt | |
| # Fail if Claude wasn't authenticated | |
| if grep -qi "not logged in\|please run /login\|authentication" /tmp/sync-report.txt; then | |
| echo "::error::Claude authentication failed. Check ANTHROPIC_API_KEY secret." | |
| exit 1 | |
| fi | |
| - name: Check for changes | |
| id: changes | |
| run: | | |
| if git diff --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then | |
| echo "has_changes=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_changes=true" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "### Changed files:" | |
| git diff --stat | |
| git ls-files --others --exclude-standard | sed 's/^/ new: /' | |
| } > /tmp/sync-summary.txt | |
| fi | |
| - name: Create PR | |
| if: steps.changes.outputs.has_changes == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| BRANCH="chore/sync-$(date +%Y-%m-%d)" | |
| # Clean up old sync branches and PRs | |
| git fetch origin | |
| for old_branch in $(git branch -r --list 'origin/chore/sync-*' | sed 's|origin/||' | xargs); do | |
| gh pr close "$old_branch" --delete-branch 2>/dev/null || true | |
| git push origin --delete "$old_branch" 2>/dev/null || true | |
| done | |
| git checkout -b "$BRANCH" | |
| git add -A | |
| git commit -m "chore: sync docs and apply audit improvements" | |
| git push -u origin "$BRANCH" | |
| # Build PR body | |
| { | |
| echo "## Changes" | |
| echo "" | |
| cat /tmp/sync-summary.txt | |
| echo "" | |
| echo "---" | |
| echo "" | |
| echo "## Sync Report" | |
| echo "" | |
| tail -40 /tmp/sync-report.txt | |
| } > /tmp/pr-body.txt | |
| gh pr create \ | |
| --title "chore: sync best practices $(date +%Y-%m-%d)" \ | |
| --body-file /tmp/pr-body.txt \ | |
| --assignee nicolas-codemate | |
| - name: Summary | |
| if: always() | |
| run: | | |
| { | |
| echo "## Sync — $(date +%Y-%m-%d)" | |
| echo "" | |
| echo "**Release** : v${{ needs.check-release.outputs.stored_version }} -> v${{ needs.check-release.outputs.latest_version }}" | |
| echo "" | |
| if [ "${{ steps.changes.outputs.has_changes }}" = "true" ]; then | |
| echo "**Statut** : PR cree avec changements" | |
| echo "" | |
| cat /tmp/sync-summary.txt | |
| else | |
| echo "**Statut** : Aucun changement detecte. Pas de PR." | |
| fi | |
| echo "" | |
| echo "---" | |
| echo "" | |
| echo "### Sync report (derniere 20 lignes)" | |
| echo "" | |
| if [ -f /tmp/sync-report.txt ]; then | |
| echo '```' | |
| tail -20 /tmp/sync-report.txt | |
| echo '```' | |
| else | |
| echo "Sync non execute ou echoue." | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" |