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: Release | ||
|
Check failure on line 1 in .github/workflows/release.yml
|
||
| on: | ||
| push: | ||
| tags: | ||
| - 'v*' | ||
| workflow_dispatch: | ||
| inputs: | ||
| version: | ||
| description: 'Release version (e.g., 1.0.1)' | ||
| required: true | ||
| type: string | ||
| prerelease: | ||
| description: 'Is this a pre-release?' | ||
| required: false | ||
| default: false | ||
| type: boolean | ||
| permissions: | ||
| contents: write | ||
| id-token: write | ||
| jobs: | ||
| validate: | ||
| name: Validate Release | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| version: ${{ steps.version.outputs.version }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Run Pester tests | ||
| shell: pwsh | ||
| run: | | ||
| if (Test-Path ./tests) { | ||
| Install-Module -Name Pester -Force -SkipPublisherCheck | ||
| Invoke-Pester ./tests -Passthru -OutputFormat NUnitXml -OutputFile TestResults.xml | ||
| } else { | ||
| Write-Host "No tests directory found, skipping tests" | ||
| } | ||
| - name: Determine version | ||
| id: version | ||
| run: | | ||
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | ||
| echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT | ||
| fi | ||
| build: | ||
| name: Build Release Artifacts | ||
| runs-on: ubuntu-latest | ||
| needs: validate | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Create release archive | ||
| run: | | ||
| VERSION="${{ needs.validate.outputs.version }}" | ||
| mkdir -p release | ||
| # Create zip archive of the toolkit | ||
| zip -r "release/wave-toolkit-${VERSION}.zip" . \ | ||
| -x ".git/*" \ | ||
| -x "release/*" \ | ||
| -x "*.zip" \ | ||
| -x ".github/*" | ||
| # Create tar.gz archive | ||
| tar --exclude='.git' --exclude='release' --exclude='*.zip' --exclude='.github' \ | ||
| -czvf "release/wave-toolkit-${VERSION}.tar.gz" . | ||
| - name: Generate checksums | ||
| run: | | ||
| cd release | ||
| sha256sum *.zip *.tar.gz > SHA256SUMS.txt | ||
| sha512sum *.zip *.tar.gz > SHA512SUMS.txt | ||
| - name: Upload artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: release-artifacts | ||
| path: release/ | ||
| retention-days: 30 | ||
| sign: | ||
| name: Sign Release Artifacts | ||
| runs-on: ubuntu-latest | ||
| needs: build | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Download artifacts | ||
| uses: actions/download-artifact@v4.1.3 | ||
| with: | ||
| name: release-artifacts | ||
| path: ./artifacts | ||
| - name: Import GPG key | ||
| if: ${{ secrets.GPG_PRIVATE_KEY != '' }} | ||
| env: | ||
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | ||
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | ||
| run: | | ||
| echo "$GPG_PRIVATE_KEY" | gpg --batch --import | ||
| echo "GPG key imported successfully" | ||
| - name: Sign artifacts | ||
| if: ${{ secrets.GPG_PRIVATE_KEY != '' }} | ||
| env: | ||
| GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} | ||
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | ||
| GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} | ||
| run: | | ||
| cd artifacts | ||
| for file in *.zip *.tar.gz SHA256SUMS.txt SHA512SUMS.txt; do | ||
| if [ -f "$file" ]; then | ||
| gpg --batch --yes --pinentry-mode loopback \ | ||
| --passphrase "$GPG_PASSPHRASE" \ | ||
| --local-user "$GPG_KEY_ID" \ | ||
| --armor --detach-sign "$file" | ||
| echo "Signed: $file" | ||
| fi | ||
| done | ||
| - name: Create unsigned notice | ||
| if: ${{ secrets.GPG_PRIVATE_KEY == '' }} | ||
| run: | | ||
| echo "# Unsigned Release" > artifacts/UNSIGNED.txt | ||
| echo "" >> artifacts/UNSIGNED.txt | ||
| echo "This release was not signed with GPG." >> artifacts/UNSIGNED.txt | ||
| echo "To enable signing, add GPG_PRIVATE_KEY, GPG_PASSPHRASE, and GPG_KEY_ID secrets." >> artifacts/UNSIGNED.txt | ||
| echo "" >> artifacts/UNSIGNED.txt | ||
| echo "See docs/RELEASE.md for instructions on setting up GPG signing." >> artifacts/UNSIGNED.txt | ||
| - name: Upload signed artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: signed-artifacts | ||
| path: ./artifacts | ||
| retention-days: 30 | ||
| release: | ||
| name: Create GitHub Release | ||
| runs-on: ubuntu-latest | ||
| needs: [validate, sign] | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Download signed artifacts | ||
| uses: actions/download-artifact@v4.1.3 | ||
| with: | ||
| name: signed-artifacts | ||
| path: ./release-files | ||
| - name: Generate release notes | ||
| id: release-notes | ||
| run: | | ||
| VERSION="${{ needs.validate.outputs.version }}" | ||
| cat > release-notes.md << 'EOF' | ||
| ## Release v${{ needs.validate.outputs.version }} | ||
| ### Installation | ||
| ```powershell | ||
| # Clone the repository | ||
| git clone https://github.com/toolate28/wave-toolkit.git | ||
| cd wave-toolkit | ||
| git checkout v${{ needs.validate.outputs.version }} | ||
| # Run setup | ||
| .\Setup-Wave.ps1 | ||
| ``` | ||
| Or download and extract the archive: | ||
| ```powershell | ||
| # Download the release | ||
| Invoke-WebRequest -Uri "https://github.com/toolate28/wave-toolkit/releases/download/v${{ needs.validate.outputs.version }}/wave-toolkit-${{ needs.validate.outputs.version }}.zip" -OutFile wave-toolkit.zip | ||
| Expand-Archive wave-toolkit.zip -DestinationPath wave-toolkit | ||
| ``` | ||
| ### Verification | ||
| To verify the release integrity: | ||
| ```powershell | ||
| # Download and run verification script | ||
| .\scripts\deployment\Verify-Release.ps1 -Version "${{ needs.validate.outputs.version }}" | ||
| ``` | ||
| Or manually: | ||
| ```bash | ||
| # Download checksums | ||
| curl -LO https://github.com/toolate28/wave-toolkit/releases/download/v${{ needs.validate.outputs.version }}/SHA256SUMS.txt | ||
| # Verify archive | ||
| sha256sum -c SHA256SUMS.txt | ||
| ``` | ||
| If GPG signatures are available: | ||
| ```bash | ||
| # Import the SpiralSafe signing key | ||
| curl -s https://spiralsafe.org/.well-known/pgp-key.txt | gpg --import | ||
| # Verify signature | ||
| gpg --verify SHA256SUMS.txt.asc SHA256SUMS.txt | ||
| ``` | ||
| ### SpiralSafe Ecosystem | ||
| This release is part of the SpiralSafe ecosystem. See the [README](https://github.com/toolate28/wave-toolkit#readme) for full documentation. | ||
| --- | ||
| *~ Hope&&Sauced* | ||
| EOF | ||
| - name: Create GitHub Release | ||
| uses: softprops/action-gh-release@v1 | ||
| with: | ||
| name: v${{ needs.validate.outputs.version }} | ||
| body_path: release-notes.md | ||
| draft: false | ||
| prerelease: ${{ inputs.prerelease || false }} | ||
| files: | | ||
| release-files/* | ||
| generate_release_notes: true | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| notify-spiralsafe: | ||
| name: Notify SpiralSafe | ||
| runs-on: ubuntu-latest | ||
| needs: [validate, release] | ||
| if: success() | ||
| steps: | ||
| - name: Notify SpiralSafe API | ||
| if: ${{ secrets.SPIRALSAFE_API_TOKEN != '' }} | ||
| env: | ||
| SPIRALSAFE_API_TOKEN: ${{ secrets.SPIRALSAFE_API_TOKEN }} | ||
| run: | | ||
| curl -X POST \ | ||
| -H "Authorization: Bearer $SPIRALSAFE_API_TOKEN" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "package": "wave-toolkit", | ||
| "version": "${{ needs.validate.outputs.version }}", | ||
| "repository": "toolate28/wave-toolkit", | ||
| "release_url": "https://github.com/toolate28/wave-toolkit/releases/tag/v${{ needs.validate.outputs.version }}" | ||
| }' \ | ||
| https://api.spiralsafe.org/v1/releases/notify || echo "SpiralSafe notification skipped (API unavailable)" | ||
| - name: Create sync marker | ||
| run: | | ||
| echo "Release v${{ needs.validate.outputs.version }} published" | ||
| echo "Sync status: pending SpiralSafe acknowledgment" | ||