Merge pull request #35 from ReneHeim/logistic-regression #75
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: Build Package | |
| on: | |
| push: | |
| branches: [main, dev] | |
| pull_request: | |
| types: [opened, synchronize, closed] | |
| branches: [main, dev] | |
| env: | |
| PYTHON_VERSION: "3.12" | |
| jobs: | |
| build: | |
| name: Build Package | |
| runs-on: ubuntu-latest | |
| if: | | |
| github.event_name == 'push' || | |
| (github.event_name == 'pull_request' && github.event.action != 'closed') || | |
| (github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true) | |
| outputs: | |
| version: ${{ steps.get_version.outputs.version }} | |
| has_artifacts: ${{ steps.check_artifacts.outputs.has_artifacts }} | |
| is_merge_commit: ${{ steps.check_merge.outputs.is_merge_commit }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| # NEW: Detect if this is a merge commit | |
| - name: Check if merge commit | |
| id: check_merge | |
| run: | | |
| # Check if commit message indicates a PR merge | |
| COMMIT_MSG=$(git log -1 --pretty=%s) | |
| echo "Commit message: $COMMIT_MSG" | |
| if [[ "$COMMIT_MSG" =~ ^Merge\ pull\ request\ #[0-9]+\ from\ .+ ]]; then | |
| echo "is_merge_commit=true" >> $GITHUB_OUTPUT | |
| echo "This is a PR merge commit" | |
| else | |
| echo "is_merge_commit=false" >> $GITHUB_OUTPUT | |
| echo "This is not a PR merge commit" | |
| fi | |
| # ... (all your existing build steps remain the same) ... | |
| - name: Set up Python ${{ env.PYTHON_VERSION }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install build dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build==1.0.3 twine==4.0.2 setuptools>=65.0 | |
| - name: Build package | |
| run: | | |
| echo "Building package..." | |
| python -m build --wheel --sdist | |
| - name: Check package integrity | |
| run: | | |
| echo "Checking package integrity..." | |
| if [ ! -d "dist" ] || [ -z "$(ls -A dist)" ]; then | |
| echo "Error: No distribution files found" | |
| exit 1 | |
| fi | |
| echo "Distribution files found:" | |
| ls -la dist/ | |
| if ! twine check dist/*; then | |
| echo "Warning: twine check failed, performing basic validation..." | |
| for file in dist/*; do | |
| if [[ $file == *.whl ]]; then | |
| echo "Checking wheel file: $file" | |
| python -m zipfile -l "$file" > /dev/null || { | |
| echo "Error: Invalid wheel file $file" | |
| exit 1 | |
| } | |
| elif [[ $file == *.tar.gz ]]; then | |
| echo "Checking source distribution: $file" | |
| tar -tzf "$file" > /dev/null || { | |
| echo "Error: Invalid source distribution $file" | |
| exit 1 | |
| } | |
| fi | |
| done | |
| echo "Basic package validation completed" | |
| else | |
| echo "Package integrity check passed" | |
| fi | |
| - name: Test package installation | |
| run: | | |
| echo "Testing package installation..." | |
| WHEEL_FILE=$(find dist -name "*.whl" | head -1) | |
| if [ -z "$WHEEL_FILE" ]; then | |
| echo "Error: No wheel file found for testing" | |
| exit 1 | |
| fi | |
| echo "Installing wheel: $WHEEL_FILE" | |
| pip install "$WHEEL_FILE" | |
| python -c " | |
| import sys | |
| print('Package installed successfully') | |
| print('Python version:', sys.version) | |
| try: | |
| print('Package import test: SKIPPED (no package specified)') | |
| except ImportError as e: | |
| print('Package import test: FAILED -', e) | |
| except Exception as e: | |
| print('Package import test: ERROR -', e) | |
| " | |
| - name: Get package version | |
| id: get_version | |
| run: | | |
| VERSION="" | |
| if [ -f "pyproject.toml" ]; then | |
| VERSION=$(grep -E '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/' | head -1) | |
| elif [ -f "setup.py" ]; then | |
| VERSION=$(python setup.py --version 2>/dev/null || echo "") | |
| fi | |
| if [ -z "$VERSION" ]; then | |
| VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "") | |
| fi | |
| if [ -z "$VERSION" ] && [ -f ".bumpversion.cfg" ]; then | |
| VERSION=$(grep "current_version" .bumpversion.cfg | cut -d'=' -f2 | tr -d ' ') | |
| fi | |
| if [ -z "$VERSION" ]; then | |
| VERSION="$(date +%Y.%m.%d)-$(git rev-parse --short HEAD)" | |
| fi | |
| echo "Detected version: $VERSION" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| - name: Check artifacts | |
| id: check_artifacts | |
| run: | | |
| if [ -d "dist" ] && [ -n "$(ls -A dist)" ]; then | |
| echo "has_artifacts=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_artifacts=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dist-${{ env.PYTHON_VERSION }}-${{ github.sha }} | |
| path: dist/ | |
| retention-days: 30 | |
| - name: Build summary | |
| if: always() | |
| run: | | |
| echo "Build Summary" | |
| echo "=============" | |
| if [ -d "dist" ] && [ -n "$(ls -A dist)" ]; then | |
| echo "Build Status: SUCCESS" | |
| echo "Files created:" | |
| for file in dist/*; do | |
| if [ -f "$file" ]; then | |
| SIZE=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo "unknown") | |
| echo " - $(basename "$file") (${SIZE} bytes)" | |
| fi | |
| done | |
| else | |
| echo "Build Status: FAILED - No distribution files created" | |
| fi | |
| echo "Build completed at: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" | |
| # UPDATED: Release job that handles both PR merges and push events | |
| release: | |
| name: Create Release | |
| runs-on: ubuntu-latest | |
| needs: build | |
| if: | | |
| needs.build.outputs.has_artifacts == 'true' && ( | |
| (github.event_name == 'pull_request' && | |
| github.event.action == 'closed' && | |
| github.event.pull_request.merged == true && | |
| github.event.pull_request.base.ref == 'main') || | |
| (github.event_name == 'push' && | |
| github.ref == 'refs/heads/main' && | |
| needs.build.outputs.is_merge_commit == 'true') | |
| ) | |
| permissions: | |
| contents: write | |
| pull-requests: read | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist-${{ env.PYTHON_VERSION }}-${{ github.sha }} | |
| path: dist/ | |
| # NEW: Get PR information for push events | |
| - name: Get PR information from merge commit | |
| id: pr_info | |
| if: github.event_name == 'push' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const commitMessage = `${{ github.event.head_commit.message }}`; | |
| console.log('Commit message:', commitMessage); | |
| // Extract PR number from merge commit message | |
| const prMatch = commitMessage.match(/Merge pull request #(\d+) from/); | |
| if (prMatch) { | |
| const prNumber = parseInt(prMatch[1]); | |
| console.log('Found PR number:', prNumber); | |
| try { | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_request_number: prNumber | |
| }); | |
| core.setOutput('pr_number', prNumber); | |
| core.setOutput('pr_title', pr.title); | |
| core.setOutput('pr_body', pr.body || ''); | |
| core.setOutput('pr_author', pr.user.login); | |
| core.setOutput('pr_merged_at', pr.merged_at); | |
| core.setOutput('pr_additions', pr.additions); | |
| core.setOutput('pr_deletions', pr.deletions); | |
| core.setOutput('pr_changed_files', pr.changed_files); | |
| return { found: true, number: prNumber }; | |
| } catch (error) { | |
| console.log('Error fetching PR:', error); | |
| return { found: false }; | |
| } | |
| } | |
| return { found: false }; | |
| - name: Generate release notes | |
| id: release_notes | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| let releaseNotes = ''; | |
| if (context.eventName === 'pull_request') { | |
| // Original PR event logic | |
| const pr = context.payload.pull_request; | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number | |
| }); | |
| const reviews = await github.paginate(github.rest.pulls.listReviews, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number | |
| }); | |
| releaseNotes = `## ${pr.title} (#${pr.number})\n\n`; | |
| if (pr.body) { | |
| releaseNotes += `### Description\n${pr.body}\n\n`; | |
| } | |
| releaseNotes += `### Pull Request Details\n`; | |
| releaseNotes += `- **Author**: @${pr.user.login}\n`; | |
| releaseNotes += `- **Merged**: ${pr.merged_at}\n`; | |
| releaseNotes += `- **Files changed**: ${pr.changed_files}\n`; | |
| releaseNotes += `- **Additions**: +${pr.additions}\n`; | |
| releaseNotes += `- **Deletions**: -${pr.deletions}\n\n`; | |
| const approvedReviews = reviews.filter(review => review.state === 'APPROVED'); | |
| if (approvedReviews.length > 0) { | |
| releaseNotes += `### Reviewers\n`; | |
| approvedReviews.forEach(review => { | |
| releaseNotes += `- @${review.user.login}\n`; | |
| }); | |
| releaseNotes += `\n`; | |
| } | |
| if (comments.length > 0) { | |
| releaseNotes += `### Discussion Summary\n`; | |
| releaseNotes += `This PR had ${comments.length} comment(s) during review.\n\n`; | |
| } | |
| } else { | |
| // Push event logic - use PR info from previous step | |
| const prNumber = '${{ steps.pr_info.outputs.pr_number }}'; | |
| const prTitle = '${{ steps.pr_info.outputs.pr_title }}'; | |
| const prBody = '${{ steps.pr_info.outputs.pr_body }}'; | |
| const prAuthor = '${{ steps.pr_info.outputs.pr_author }}'; | |
| const prMergedAt = '${{ steps.pr_info.outputs.pr_merged_at }}'; | |
| const prAdditions = '${{ steps.pr_info.outputs.pr_additions }}'; | |
| const prDeletions = '${{ steps.pr_info.outputs.pr_deletions }}'; | |
| const prChangedFiles = '${{ steps.pr_info.outputs.pr_changed_files }}'; | |
| if (prNumber) { | |
| releaseNotes = `## ${prTitle} (#${prNumber})\n\n`; | |
| if (prBody) { | |
| releaseNotes += `### Description\n${prBody}\n\n`; | |
| } | |
| releaseNotes += `### Pull Request Details\n`; | |
| releaseNotes += `- **Author**: @${prAuthor}\n`; | |
| releaseNotes += `- **Merged**: ${prMergedAt}\n`; | |
| releaseNotes += `- **Files changed**: ${prChangedFiles}\n`; | |
| releaseNotes += `- **Additions**: +${prAdditions}\n`; | |
| releaseNotes += `- **Deletions**: -${prDeletions}\n\n`; | |
| } else { | |
| releaseNotes = `## Release from commit ${context.sha.substring(0, 7)}\n\n`; | |
| releaseNotes += `### Commit Details\n`; | |
| releaseNotes += `- **Message**: ${{ github.event.head_commit.message }}\n`; | |
| releaseNotes += `- **Author**: @${{ github.event.head_commit.author.username }}\n`; | |
| releaseNotes += `- **Timestamp**: ${{ github.event.head_commit.timestamp }}\n\n`; | |
| } | |
| } | |
| releaseNotes += `### Build Information\n`; | |
| releaseNotes += `- **Python Version**: ${{ env.PYTHON_VERSION }}\n`; | |
| releaseNotes += `- **Built on**: ${new Date().toISOString()}\n`; | |
| releaseNotes += `- **Commit**: ${context.sha}\n`; | |
| releaseNotes += `- **Triggered by**: ${context.eventName}\n`; | |
| core.setOutput('notes', releaseNotes); | |
| - name: Create GitHub Release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ needs.build.outputs.version }}" | |
| TAG_NAME="v$VERSION" | |
| if ! git tag -l | grep -q "^$TAG_NAME$"; then | |
| git config --local user.name "github-actions[bot]" | |
| git config --local user.email "github-actions[bot]@users.noreply.github.com" | |
| git tag "$TAG_NAME" | |
| git push origin "$TAG_NAME" | |
| fi | |
| cat << 'EOF' > release_notes.md | |
| ${{ steps.release_notes.outputs.notes }} | |
| EOF | |
| gh release create "$TAG_NAME" \ | |
| --title "Release $VERSION" \ | |
| --notes-file release_notes.md \ | |
| --latest \ | |
| dist/* | |
| - name: Release summary | |
| run: | | |
| echo "Release Created Successfully!" | |
| echo "================================" | |
| echo "Version: ${{ needs.build.outputs.version }}" | |
| echo "Tag: v${{ needs.build.outputs.version }}" | |
| echo "Triggered by: ${{ github.event_name }}" | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| echo "PR: #${{ steps.pr_info.outputs.pr_number }}" | |
| echo "Author: @${{ steps.pr_info.outputs.pr_author }}" | |
| else | |
| echo "PR: #${{ github.event.pull_request.number }}" | |
| echo "Author: @${{ github.event.pull_request.user.login }}" | |
| fi | |
| echo "Files included:" | |
| ls -la dist/ |