Publish Python 🐍 distributions 📦 to PyPI and TestPyPI #26
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: Publish Python 🐍 distributions 📦 to PyPI and TestPyPI | |
| on: | |
| push: | |
| tags: | |
| - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 | |
| # Allows to run this workflow manually from the Actions tab on GitHub UI | |
| workflow_dispatch: | |
| jobs: | |
| check-quality-gate: | |
| name: Check 🛡️ Bastion Quality Gates | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Wait for 🛡️ Bastion Quality Gates to complete | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} | |
| PROJECT_KEY: DavidOsipov_PostQuantum-Feldman-VSS | |
| GITHUB_SHA: ${{ github.sha }} | |
| run: | | |
| echo "Waiting for 🛡️ Bastion Quality Gates analysis to complete for commit: $GITHUB_SHA" | |
| MAX_ATTEMPTS=30 # 30 attempts × 30 seconds = 15 minutes maximum wait time | |
| ATTEMPT=1 | |
| while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do | |
| echo "Attempt $ATTEMPT of $MAX_ATTEMPTS: Checking for completed analysis..." | |
| # Use the qualitygates/project_status endpoint instead | |
| ANALYSIS_RESPONSE=$(curl --silent --fail --show-error \ | |
| --header "Authorization: Bearer ${SONAR_TOKEN}" \ | |
| "${SONAR_HOST_URL}/api/qualitygates/project_status?projectKey=${PROJECT_KEY}") | |
| # Check if request was successful | |
| if [ $? -ne 0 ]; then | |
| echo "Failed to query SonarQube API for project status" | |
| sleep 30 | |
| ATTEMPT=$((ATTEMPT+1)) | |
| continue | |
| fi | |
| # Extract status | |
| STATUS=$(echo $ANALYSIS_RESPONSE | jq -r '.projectStatus.status') | |
| if [ "$STATUS" == "OK" ] || [ "$STATUS" == "ERROR" ] || [ "$STATUS" == "WARN" ]; then | |
| echo "✅ Analysis complete with status: $STATUS" | |
| if [ "$STATUS" == "OK" ]; then | |
| exit 0 | |
| else | |
| echo "::warning::Analysis completed but with status: $STATUS" | |
| exit 0 # Still allow the workflow to continue | |
| fi | |
| fi | |
| echo "Analysis not yet complete. Waiting 30 seconds before next check..." | |
| sleep 30 | |
| ATTEMPT=$((ATTEMPT+1)) | |
| done | |
| echo "::error::Timed out waiting for SonarQube analysis to complete after $(($MAX_ATTEMPTS * 30)) seconds" | |
| echo "::error::Please ensure the SonarQube analysis is configured correctly." | |
| exit 1 | |
| - name: Check 🛡️ Bastion Quality Gates | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} | |
| PROJECT_KEY: DavidOsipov_PostQuantum-Feldman-VSS | |
| run: | | |
| echo "Checking quality gate status for project: ${PROJECT_KEY}" | |
| # Get quality gate status using the projectKey parameter | |
| QUALITY_GATE_RESPONSE=$(curl --silent --fail --show-error \ | |
| --header "Authorization: Bearer ${SONAR_TOKEN}" \ | |
| "${SONAR_HOST_URL}/api/qualitygates/project_status?projectKey=${PROJECT_KEY}") | |
| # Check if request was successful | |
| if [ $? -ne 0 ]; then | |
| echo "::error::Failed to query SonarQube quality gate status" | |
| exit 1 | |
| fi | |
| # Extract and check quality gate status | |
| STATUS=$(echo $QUALITY_GATE_RESPONSE | jq -r '.projectStatus.status') | |
| echo "Quality Gate Status: $STATUS" | |
| # Print detailed condition information when status is not OK | |
| if [ "$STATUS" != "OK" ]; then | |
| echo "::group::Quality Gate Failure Details" | |
| echo "Failed conditions:" | |
| echo $QUALITY_GATE_RESPONSE | jq -r '.projectStatus.conditions[] | select(.status != "OK") | "- Metric: \(.metricKey), Status: \(.status), Error threshold: \(.errorThreshold), Actual value: \(.actualValue)"' | |
| echo "::endgroup::" | |
| # Get project issues summary | |
| ISSUES_RESPONSE=$(curl --silent --fail --show-error \ | |
| --header "Authorization: Bearer ${SONAR_TOKEN}" \ | |
| "${SONAR_HOST_URL}/api/issues/search?projectKeys=${PROJECT_KEY}&resolved=false&severities=BLOCKER,CRITICAL&ps=10") | |
| echo "::group::Critical Issues (showing up to 10)" | |
| echo $ISSUES_RESPONSE | jq -r '.issues[] | "- [\(.severity)] \(.message) | In file: \(.component)"' | |
| echo "::endgroup::" | |
| echo "::error::Quality Gate check failed with status: $STATUS" | |
| echo "::error::Please check SonarQube analysis at ${SONAR_HOST_URL}/dashboard?id=${PROJECT_KEY}" | |
| exit 1 | |
| fi | |
| echo "✅ Quality Gate passed successfully" | |
| build-n-publish: | |
| name: pypi-publish | |
| needs: check-quality-gate | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: pypi | |
| permissions: | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 | |
| - name: Set up Python | |
| uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 | |
| with: | |
| python-version: "3.13" | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install hatch | |
| - name: Build a binary wheel and a source tarball | |
| run: | | |
| hatch build | |
| - name: Publish distribution 📦 to PyPI | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: pypa/gh-action-pypi-publish@db8f07d3871a0a180efa06b95d467625c19d5d5f | |
| with: | |
| print-hash: true | |
| verbose: true |