mirror of
https://github.com/alirezarezvani/ClaudeForge.git
synced 2026-07-03 18:33:16 -04:00
dd6a6c24d7
Phase 1: Core GitHub Workflows Implementation Composite Actions (4): - setup-python-deps: Cache Python dependencies for faster runs - fork-safety: Detect fork PRs and prevent malicious write operations - rate-limit-check: Circuit breaker pattern for GitHub API exhaustion - quality-gates: Python syntax, Markdown lint, Bash validation, secret scanning Workflows (5): - bootstrap.yml: One-time repository setup (labels, milestones, settings) - reusable-pr-checks.yml: DRY quality gate orchestrator - pr-into-dev.yml: Feature PR validation (branch names, conventional commits, linked issues) - dev-to-main.yml: Release gate validation (source branch, CHANGELOG, production readiness) - release.yml: Manual release creation with GitHub releases and auto-generated notes Branch Strategy: Standard (feature/* → dev → main) Quality Gates: Python, Markdown, Bash, Secrets Release Trigger: Manual via /release command or workflow_dispatch Implements comprehensive CI/CD system adapted from blueprint: - Fork safety and rate limiting for security - Conventional commits enforcement - Automated quality validation - Production release gates - GitHub release automation Next: Phase 2 (templates, CODEOWNERS, dependabot)
271 lines
9.6 KiB
YAML
271 lines
9.6 KiB
YAML
name: 'Create Release'
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'Release version (e.g., 1.1.0)'
|
|
required: true
|
|
type: string
|
|
prerelease:
|
|
description: 'Mark as pre-release'
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
draft:
|
|
description: 'Create as draft'
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
|
|
permissions:
|
|
contents: write
|
|
pull-requests: read
|
|
issues: write
|
|
|
|
jobs:
|
|
validate-release:
|
|
name: Validate Release
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Validate version format
|
|
id: validate-version
|
|
run: |
|
|
VERSION="${{ inputs.version }}"
|
|
echo "Validating version: $VERSION"
|
|
|
|
# Check semantic versioning format
|
|
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
|
echo "✅ Version format is valid"
|
|
echo "valid=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "::error::Invalid version format: $VERSION"
|
|
echo "::error::Must be semantic versioning (e.g., 1.1.0 or 1.1.0-beta.1)"
|
|
echo "valid=false" >> $GITHUB_OUTPUT
|
|
exit 1
|
|
fi
|
|
|
|
- name: Check if tag already exists
|
|
id: check-tag
|
|
run: |
|
|
VERSION="${{ inputs.version }}"
|
|
TAG="v$VERSION"
|
|
|
|
if git rev-parse "$TAG" >/dev/null 2>&1; then
|
|
echo "::error::Tag $TAG already exists"
|
|
echo "::error::Please use a different version number"
|
|
echo "exists=true" >> $GITHUB_OUTPUT
|
|
exit 1
|
|
else
|
|
echo "✅ Tag $TAG is available"
|
|
echo "exists=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Validate CHANGELOG.md
|
|
id: check-changelog
|
|
run: |
|
|
VERSION="${{ inputs.version }}"
|
|
|
|
if [ ! -f "CHANGELOG.md" ]; then
|
|
echo "::warning::CHANGELOG.md not found"
|
|
echo "updated=false" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
|
|
# Check if version is mentioned in CHANGELOG
|
|
if grep -q "\[$VERSION\]" CHANGELOG.md || grep -q "## $VERSION" CHANGELOG.md; then
|
|
echo "✅ Version $VERSION found in CHANGELOG.md"
|
|
echo "updated=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "::warning::Version $VERSION not found in CHANGELOG.md"
|
|
echo "::warning::Consider adding release notes before creating release"
|
|
echo "updated=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
create-release:
|
|
name: Create GitHub Release
|
|
needs: validate-release
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Rate limit check
|
|
uses: ./.github/actions/rate-limit-check
|
|
with:
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
minimum-remaining: 100
|
|
|
|
- name: Extract release notes from CHANGELOG
|
|
id: extract-notes
|
|
run: |
|
|
VERSION="${{ inputs.version }}"
|
|
|
|
if [ ! -f "CHANGELOG.md" ]; then
|
|
echo "CHANGELOG.md not found, using default release notes"
|
|
NOTES="Release v$VERSION
|
|
|
|
See the full changelog at https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md"
|
|
else
|
|
# Try to extract notes for this version
|
|
# Look for section between [VERSION] and next [VERSION] or end of file
|
|
NOTES=$(awk "/\[$VERSION\]|## $VERSION/,/^## \[|^## [0-9]/" CHANGELOG.md | tail -n +2 | head -n -1)
|
|
|
|
if [ -z "$NOTES" ]; then
|
|
echo "No specific notes found for $VERSION in CHANGELOG.md"
|
|
NOTES="Release v$VERSION
|
|
|
|
See the full changelog at https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md"
|
|
else
|
|
echo "✅ Extracted release notes from CHANGELOG.md"
|
|
fi
|
|
fi
|
|
|
|
# Save to file for GitHub release
|
|
echo "$NOTES" > /tmp/release-notes.md
|
|
echo "notes-file=/tmp/release-notes.md" >> $GITHUB_OUTPUT
|
|
|
|
- name: Get commits since last release
|
|
id: get-commits
|
|
run: |
|
|
# Get the last release tag
|
|
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
|
|
if [ -z "$LAST_TAG" ]; then
|
|
echo "No previous tags found, getting all commits"
|
|
COMMITS=$(git log --pretty=format:"- %s (%h)" --no-merges)
|
|
else
|
|
echo "Getting commits since $LAST_TAG"
|
|
COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges)
|
|
fi
|
|
|
|
echo "$COMMITS" > /tmp/commits.txt
|
|
echo "commits-file=/tmp/commits.txt" >> $GITHUB_OUTPUT
|
|
|
|
- name: Create release notes
|
|
run: |
|
|
VERSION="${{ inputs.version }}"
|
|
TAG="v$VERSION"
|
|
|
|
# Combine CHANGELOG notes with commit list
|
|
cat /tmp/release-notes.md > /tmp/final-notes.md
|
|
|
|
echo "" >> /tmp/final-notes.md
|
|
echo "---" >> /tmp/final-notes.md
|
|
echo "" >> /tmp/final-notes.md
|
|
echo "## 📦 Installation" >> /tmp/final-notes.md
|
|
echo "" >> /tmp/final-notes.md
|
|
echo "### One-Line Install (Recommended)" >> /tmp/final-notes.md
|
|
echo '```bash' >> /tmp/final-notes.md
|
|
echo "curl -fsSL https://raw.githubusercontent.com/${{ github.repository }}/main/install.sh | bash" >> /tmp/final-notes.md
|
|
echo '```' >> /tmp/final-notes.md
|
|
echo "" >> /tmp/final-notes.md
|
|
echo "### Manual Install" >> /tmp/final-notes.md
|
|
echo '```bash' >> /tmp/final-notes.md
|
|
echo "wget https://github.com/${{ github.repository }}/archive/refs/tags/$TAG.tar.gz" >> /tmp/final-notes.md
|
|
echo "tar -xzf $TAG.tar.gz" >> /tmp/final-notes.md
|
|
echo "cd ClaudeForge-${VERSION}" >> /tmp/final-notes.md
|
|
echo "./install.sh" >> /tmp/final-notes.md
|
|
echo '```' >> /tmp/final-notes.md
|
|
echo "" >> /tmp/final-notes.md
|
|
echo "📚 **Documentation**: https://github.com/${{ github.repository }}/blob/main/docs/INSTALLATION.md" >> /tmp/final-notes.md
|
|
echo "" >> /tmp/final-notes.md
|
|
|
|
if [ -s /tmp/commits.txt ]; then
|
|
echo "## 📝 Commits" >> /tmp/final-notes.md
|
|
echo "" >> /tmp/final-notes.md
|
|
cat /tmp/commits.txt >> /tmp/final-notes.md
|
|
echo "" >> /tmp/final-notes.md
|
|
fi
|
|
|
|
echo "---" >> /tmp/final-notes.md
|
|
echo "" >> /tmp/final-notes.md
|
|
echo "**Full Changelog**: https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md" >> /tmp/final-notes.md
|
|
|
|
- name: Create GitHub Release
|
|
id: create-release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
VERSION="${{ inputs.version }}"
|
|
TAG="v$VERSION"
|
|
DRAFT_FLAG=""
|
|
PRERELEASE_FLAG=""
|
|
|
|
if [[ "${{ inputs.draft }}" == "true" ]]; then
|
|
DRAFT_FLAG="--draft"
|
|
fi
|
|
|
|
if [[ "${{ inputs.prerelease }}" == "true" ]]; then
|
|
PRERELEASE_FLAG="--prerelease"
|
|
fi
|
|
|
|
gh release create "$TAG" \
|
|
--title "ClaudeForge $TAG" \
|
|
--notes-file /tmp/final-notes.md \
|
|
$DRAFT_FLAG \
|
|
$PRERELEASE_FLAG
|
|
|
|
echo "✅ Release $TAG created successfully"
|
|
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
|
|
|
- name: Update installation script references
|
|
if: inputs.prerelease == false && inputs.draft == false
|
|
run: |
|
|
VERSION="${{ inputs.version }}"
|
|
echo "::notice::Consider updating installer script to reference $VERSION"
|
|
echo "::notice::Files to update: install.sh, install.ps1 (if they contain version references)"
|
|
|
|
release-summary:
|
|
name: Release Summary
|
|
needs: [validate-release, create-release]
|
|
runs-on: ubuntu-latest
|
|
if: always()
|
|
|
|
steps:
|
|
- name: Generate summary
|
|
run: |
|
|
VERSION="${{ inputs.version }}"
|
|
TAG="v$VERSION"
|
|
|
|
echo "## 🎉 Release Summary" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
if [[ "${{ needs.validate-release.result }}" == "success" ]] && \
|
|
[[ "${{ needs.create-release.result }}" == "success" ]]; then
|
|
echo "### ✅ Release $TAG Created Successfully!" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "🔗 **Release URL**: https://github.com/${{ github.repository }}/releases/tag/$TAG" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### Next Steps" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "1. ✅ Release created on GitHub" >> $GITHUB_STEP_SUMMARY
|
|
|
|
if [[ "${{ inputs.draft }}" == "true" ]]; then
|
|
echo "2. ⚠️ Release is in **DRAFT** mode - publish when ready" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
|
|
if [[ "${{ inputs.prerelease }}" == "true" ]]; then
|
|
echo "3. ⚠️ Marked as **PRE-RELEASE**" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "4. Consider announcing the release:" >> $GITHUB_STEP_SUMMARY
|
|
echo " - Update README.md badges if needed" >> $GITHUB_STEP_SUMMARY
|
|
echo " - Post announcement in Discussions" >> $GITHUB_STEP_SUMMARY
|
|
echo " - Share on social media if applicable" >> $GITHUB_STEP_SUMMARY
|
|
else
|
|
echo "### ❌ Release Creation Failed" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "Please check the workflow logs for details." >> $GITHUB_STEP_SUMMARY
|
|
fi
|