diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5c887f5..6ae7b9b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,43 +10,196 @@ permissions: pull-requests: write jobs: - release-please: - name: Release Please + release: + name: Auto Release runs-on: ubuntu-latest - outputs: - release_created: ${{ steps.release.outputs.release_created }} - tag_name: ${{ steps.release.outputs.tag_name }} - pr: ${{ steps.release.outputs.pr }} - steps: - - name: Release Please - id: release - uses: googleapis/release-please-action@v4 - with: - release-type: simple - include-v-in-tag: true - changelog-types: > - [ - {"type": "feat", "section": "Features", "hidden": false}, - {"type": "fix", "section": "Bug Fixes", "hidden": false}, - {"type": "perf", "section": "Performance", "hidden": false}, - {"type": "docs", "section": "Documentation", "hidden": false}, - {"type": "chore", "section": "Maintenance", "hidden": true}, - {"type": "style", "section": "Styles", "hidden": true}, - {"type": "refactor", "section": "Refactoring", "hidden": true} - ] - - validate: - name: Validate Release - runs-on: ubuntu-latest - needs: release-please - if: ${{ needs.release-please.outputs.release_created }} steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get latest tag + id: get_tag + run: | + # Get latest semver tag (v*.*.*) + LATEST_TAG=$(git tag -l "v[0-9]*.[0-9]*.[0-9]*" | sort -V | tail -1) + if [ -z "$LATEST_TAG" ]; then + LATEST_TAG="v0.0.0" + fi + echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT + echo "Latest tag: $LATEST_TAG" + + - name: Analyze commits and determine version + id: version + run: | + LATEST_TAG="${{ steps.get_tag.outputs.latest_tag }}" + + # Get commits since last tag + if [ "$LATEST_TAG" = "v0.0.0" ]; then + COMMITS=$(git log --pretty=format:"%s" HEAD) + else + COMMITS=$(git log --pretty=format:"%s" ${LATEST_TAG}..HEAD) + fi - - name: Validate Scripts + if [ -z "$COMMITS" ]; then + echo "No new commits since $LATEST_TAG" + echo "skip=true" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "Commits since $LATEST_TAG:" + echo "$COMMITS" + + # Determine version bump from conventional commits + MAJOR=0 + MINOR=0 + PATCH=0 + + while IFS= read -r commit; do + if echo "$commit" | grep -qE "^feat(\(.+\))?!:|^fix(\(.+\))?!:|^refactor(\(.+\))?!:|BREAKING CHANGE"; then + MAJOR=1 + elif echo "$commit" | grep -qE "^feat(\(.+\))?:"; then + MINOR=1 + elif echo "$commit" | grep -qE "^fix(\(.+\))?:|^perf(\(.+\))?:|^refactor(\(.+\))?:"; then + PATCH=1 + fi + done <<< "$COMMITS" + + # Parse current version + VERSION="${LATEST_TAG#v}" + IFS='.' read -r CUR_MAJOR CUR_MINOR CUR_PATCH <<< "$VERSION" + + # Calculate new version + if [ "$MAJOR" -eq 1 ]; then + NEW_VERSION="$((CUR_MAJOR + 1)).0.0" + BUMP_TYPE="major" + elif [ "$MINOR" -eq 1 ]; then + NEW_VERSION="${CUR_MAJOR}.$((CUR_MINOR + 1)).0" + BUMP_TYPE="minor" + elif [ "$PATCH" -eq 1 ]; then + NEW_VERSION="${CUR_MAJOR}.${CUR_MINOR}.$((CUR_PATCH + 1))" + BUMP_TYPE="patch" + else + echo "No releasable commits (need feat:, fix:, perf:, refactor:, or breaking change)" + echo "skip=true" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "new_version=v${NEW_VERSION}" >> $GITHUB_OUTPUT + echo "bump_type=$BUMP_TYPE" >> $GITHUB_OUTPUT + echo "skip=false" >> $GITHUB_OUTPUT + echo "New version: v${NEW_VERSION} ($BUMP_TYPE bump)" + + - name: Generate release notes + if: steps.version.outputs.skip != 'true' + id: notes run: | - echo "🚀 Validating ${{ needs.release-please.outputs.tag_name }}..." + LATEST_TAG="${{ steps.get_tag.outputs.latest_tag }}" + NEW_VERSION="${{ steps.version.outputs.new_version }}" + + # Generate notes from commits + NOTES="" + FEATURES="" + FIXES="" + PERF="" + OTHER="" + + if [ "$LATEST_TAG" = "v0.0.0" ]; then + COMMITS=$(git log --pretty=format:"%s|%h" HEAD) + else + COMMITS=$(git log --pretty=format:"%s|%h" ${LATEST_TAG}..HEAD) + fi + + while IFS='|' read -r msg hash; do + if echo "$msg" | grep -qE "^feat(\(.+\))?:"; then + CLEAN_MSG=$(echo "$msg" | sed -E 's/^feat(\([^)]+\))?:\s*//') + FEATURES="${FEATURES}- ${CLEAN_MSG} (${hash})\n" + elif echo "$msg" | grep -qE "^fix(\(.+\))?:"; then + CLEAN_MSG=$(echo "$msg" | sed -E 's/^fix(\([^)]+\))?:\s*//') + FIXES="${FIXES}- ${CLEAN_MSG} (${hash})\n" + elif echo "$msg" | grep -qE "^perf(\(.+\))?:"; then + CLEAN_MSG=$(echo "$msg" | sed -E 's/^perf(\([^)]+\))?:\s*//') + PERF="${PERF}- ${CLEAN_MSG} (${hash})\n" + fi + done <<< "$COMMITS" + + # Build release notes + if [ -n "$FEATURES" ]; then + NOTES="${NOTES}## Features\n${FEATURES}\n" + fi + if [ -n "$FIXES" ]; then + NOTES="${NOTES}## Bug Fixes\n${FIXES}\n" + fi + if [ -n "$PERF" ]; then + NOTES="${NOTES}## Performance\n${PERF}\n" + fi + + # Add installation instructions + NOTES="${NOTES}## Installation\n\`\`\`bash\ncurl -fsSL https://raw.githubusercontent.com/draphy/draphyOS/latest/install.sh | bash\n\`\`\`" + + # Save to file (handle multiline) + echo -e "$NOTES" > /tmp/release_notes.md + echo "Generated release notes" + + - name: Validate scripts + if: steps.version.outputs.skip != 'true' + run: | + echo "Validating shell scripts..." bash -n install.sh bash -n uninstall.sh echo "✅ Scripts are valid" + + - name: Create tag and release + if: steps.version.outputs.skip != 'true' + run: | + NEW_VERSION="${{ steps.version.outputs.new_version }}" + + # Configure git + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Create and push tag + git tag "$NEW_VERSION" + git push origin "$NEW_VERSION" + + # Create GitHub release + gh release create "$NEW_VERSION" \ + --title "$NEW_VERSION" \ + --notes-file /tmp/release_notes.md \ + --latest + + echo "✅ Released $NEW_VERSION" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Comment on merged PRs + if: steps.version.outputs.skip != 'true' + run: | + LATEST_TAG="${{ steps.get_tag.outputs.latest_tag }}" + NEW_VERSION="${{ steps.version.outputs.new_version }}" + + # Get PR numbers from commits since last tag + if [ "$LATEST_TAG" = "v0.0.0" ]; then + COMMITS=$(git log --pretty=format:"%H" HEAD) + else + COMMITS=$(git log --pretty=format:"%H" ${LATEST_TAG}..HEAD) + fi + + # Find PRs associated with these commits + COMMENTED_PRS="" + while IFS= read -r sha; do + # Get PR number for this commit + PR_DATA=$(gh pr list --state merged --search "$sha" --json number --jq '.[0].number' 2>/dev/null || echo "") + + if [ -n "$PR_DATA" ] && [ "$PR_DATA" != "null" ]; then + # Avoid duplicate comments + if [[ ! "$COMMENTED_PRS" =~ "$PR_DATA" ]]; then + gh pr comment "$PR_DATA" --body "🚀 Released in [$NEW_VERSION](https://github.com/${{ github.repository }}/releases/tag/$NEW_VERSION)" 2>/dev/null || true + COMMENTED_PRS="${COMMENTED_PRS} ${PR_DATA}" + echo "Commented on PR #$PR_DATA" + fi + fi + done <<< "$COMMITS" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}