.github: add bitSign workflow for signing releases #3
Workflow file for this run
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: bitSign Infix Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_version: | |
| description: 'Release version (e.g., v25.08.0)' | |
| required: true | |
| type: string | |
| push: | |
| branches: [ sign-with-bitsign ] | |
| jobs: | |
| sign-infix-release: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Validate release version format | |
| id: validate | |
| run: | | |
| # Use default version for push events, input version for manual dispatch | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| VERSION="v25.08.0" | |
| echo "Using default version for push trigger: $VERSION" | |
| else | |
| VERSION="${{ inputs.release_version }}" | |
| echo "Using input version for manual trigger: $VERSION" | |
| fi | |
| # Check if version starts with 'v' and has proper format | |
| if ! echo "$VERSION" | grep -qE '^v[0-9]+\.[0-9]+(\.[0-9]+)?(-alpha[0-9]*|-beta[0-9]*|-rc[0-9]*)?$'; then | |
| echo "❌ Invalid version format. Expected format: vYY.MM(.PP)(-alphaN|-betaN|-rcN)" | |
| echo "Examples: v25.08.0, v25.08.0-alpha1, v25.08.0-rc1" | |
| exit 1 | |
| fi | |
| # Extract version without 'v' prefix for filename | |
| FILE_VERSION="${VERSION#v}" | |
| FILENAME="infix-x86_64-${FILE_VERSION}.tar.gz" | |
| RELEASE_URL="https://github.com/kernelkit/infix/releases/download/${VERSION}/${FILENAME}" | |
| echo "✅ Version format valid" | |
| echo "file_version=${FILE_VERSION}" >> $GITHUB_OUTPUT | |
| echo "filename=${FILENAME}" >> $GITHUB_OUTPUT | |
| echo "release_url=${RELEASE_URL}" >> $GITHUB_OUTPUT | |
| - name: Download Infix release | |
| run: | | |
| RELEASE_URL="${{ steps.validate.outputs.release_url }}" | |
| FILENAME="${{ steps.validate.outputs.filename }}" | |
| echo "Downloading Infix release: $FILENAME" | |
| echo "From: $RELEASE_URL" | |
| if ! curl -L "$RELEASE_URL" -o "$FILENAME" --fail --show-error --progress-bar; then | |
| echo "❌ Failed to download release file from: $RELEASE_URL" | |
| echo "Please verify that the release version exists and contains the x86_64 build." | |
| echo "You can check available releases at: https://github.com/kernelkit/infix/releases" | |
| exit 1 | |
| fi | |
| # Verify download | |
| if [ -f "$FILENAME" ] && [ -s "$FILENAME" ]; then | |
| FILE_SIZE=$(ls -lh "$FILENAME" | awk '{print $5}') | |
| echo "✅ Successfully downloaded: $FILENAME ($FILE_SIZE)" | |
| else | |
| echo "❌ Failed to download release file" | |
| exit 1 | |
| fi | |
| - name: Create signing request | |
| id: create_request | |
| run: | | |
| FILENAME="${{ steps.validate.outputs.filename }}" | |
| FILE_VERSION="${{ steps.validate.outputs.file_version }}" | |
| RELEASE="infix-x86_64-${FILE_VERSION}" | |
| echo "Creating signing request for $FILENAME..." | |
| echo "RELEASE parameter: $RELEASE" | |
| # Create signing request using bitSign API | |
| RESPONSE=$(curl -s -X POST "https://portal.bitsign.se/api/v1/requests" \ | |
| -H "Authorization: Bearer ${{ secrets.BITSIGN_API_KEY }}" \ | |
| -F "file=@$FILENAME" \ | |
| -F "key=bit42-Demo1" \ | |
| -F "job=Infix-x86" \ | |
| -F "parameters={\"RELEASE\": \"$RELEASE\"}") | |
| echo "API Response: $RESPONSE" | |
| # Check if request was successful | |
| if echo "$RESPONSE" | jq -e '.success == true' > /dev/null; then | |
| REQUEST_ID=$(echo "$RESPONSE" | jq -r '.requestId') | |
| echo "✅ Request created successfully with ID: $REQUEST_ID" | |
| echo "request_id=$REQUEST_ID" >> $GITHUB_OUTPUT | |
| else | |
| echo "❌ Failed to create signing request" | |
| echo "$RESPONSE" | jq -r '.error // .message // "Unknown error"' | |
| exit 1 | |
| fi | |
| - name: Wait for signing completion | |
| id: wait_completion | |
| run: | | |
| REQUEST_ID="${{ steps.create_request.outputs.request_id }}" | |
| echo "Waiting for signing completion for request: $REQUEST_ID" | |
| # Poll status for up to 10 minutes (600 seconds) - longer timeout for larger files | |
| MAX_WAIT=600 | |
| WAITED=0 | |
| while [ $WAITED -lt $MAX_WAIT ]; do | |
| RESPONSE=$(curl -s -X GET "https://portal.bitsign.se/api/v1/requests/$REQUEST_ID/status" \ | |
| -H "Authorization: Bearer ${{ secrets.BITSIGN_API_KEY }}") | |
| STATUS=$(echo "$RESPONSE" | jq -r '.status') | |
| echo "Current status: $STATUS (waited ${WAITED}s)" | |
| if [ "$STATUS" = "completed" ]; then | |
| echo "✅ Signing completed successfully!" | |
| break | |
| elif [ "$STATUS" = "failed" ]; then | |
| echo "❌ Signing failed" | |
| echo "$RESPONSE" | jq -r '.error // .message // "Unknown error"' | |
| exit 1 | |
| fi | |
| sleep 10 | |
| WAITED=$((WAITED + 10)) | |
| done | |
| if [ $WAITED -ge $MAX_WAIT ]; then | |
| echo "❌ Timeout: Signing did not complete within ${MAX_WAIT} seconds" | |
| exit 1 | |
| fi | |
| - name: Download signed file | |
| id: download_signed | |
| run: | | |
| REQUEST_ID="${{ steps.create_request.outputs.request_id }}" | |
| FILENAME="${{ steps.validate.outputs.filename }}" | |
| FILE_VERSION="${{ steps.validate.outputs.file_version }}" | |
| SIGNED_FILENAME="infix-x86_64-${FILE_VERSION}-signed.tar.gz" | |
| echo "Downloading signed file for request: $REQUEST_ID" | |
| echo "Expected signed filename: $SIGNED_FILENAME" | |
| # Download the signed file | |
| curl -X GET "https://portal.bitsign.se/api/v1/requests/$REQUEST_ID/download" \ | |
| -H "Authorization: Bearer ${{ secrets.BITSIGN_API_KEY }}" \ | |
| -o "$SIGNED_FILENAME" \ | |
| --fail --show-error | |
| if [ -f "$SIGNED_FILENAME" ] && [ -s "$SIGNED_FILENAME" ]; then | |
| FILE_SIZE=$(ls -lh "$SIGNED_FILENAME" | awk '{print $5}') | |
| echo "Successfully downloaded signed file: $SIGNED_FILENAME ($FILE_SIZE)" | |
| echo "signed_filename=$SIGNED_FILENAME" >> $GITHUB_OUTPUT | |
| else | |
| echo "Failed to download signed file" | |
| exit 1 | |
| fi | |
| - name: Upload signed artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: signed-infix-${{ steps.validate.outputs.file_version }} | |
| path: | | |
| ${{ steps.validate.outputs.filename }} | |
| ${{ steps.download_signed.outputs.signed_filename }} | |
| retention-days: 90 | |
| - name: Summary | |
| run: | | |
| cat >> $GITHUB_STEP_SUMMARY << 'EOF' | |
| # bitSign Infix Release Signing Complete | |
| The Infix release has been successfully signed using the bitSign API. | |
| ## Process Overview | |
| 1. **Release Download** - Downloaded specified Infix release from GitHub | |
| 2. **Signing Request** - Created signing request via bitSign API | |
| 3. **Approval Process** - Trusted signers received notification and approved with 2FA | |
| 4. **bitSign Processing** - Backend securely signed the release file | |
| 5. **Download & Store** - Retrieved signed result and stored as artifacts | |
| ## Signing Details | |
| | Property | Value | | |
| |----------|-------| | |
| | 📦 **Original File** | `${{ steps.validate.outputs.filename }}` | | |
| | ✍️ **Signed File** | `${{ steps.download_signed.outputs.signed_filename }}` | | |
| | 🔑 **Signing Key** | `bit42-Demo1` | | |
| | ⚙️ **Job Type** | `Infix-x86` | | |
| | 🆔 **Request ID** | `${{ steps.create_request.outputs.request_id }}` | | |
| | 📋 **Release Version** | `${{ inputs.release_version }}` | | |
| | 🌐 **API Endpoint** | `portal.bitsign.se` | | |
| ## Download Files | |
| > **Both the original release file and signed version are available as a workflow artifact:** | |
| > **`signed-infix-${{ steps.validate.outputs.file_version }}`** | |
| > | |
| > The artifact contains both `${{ steps.validate.outputs.filename }}` and `${{ steps.download_signed.outputs.signed_filename }}` files. | |
| --- | |
| **Next Steps:** The signed release can now be distributed with cryptographic verification of authenticity. | |
| EOF |