Handle base64 whitespace when importing PGP key #21
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
| # Release - Automated publishing to Maven Central | |
| # Mirrored from toon4s, tailored for FlowForge build commands | |
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| repository_dispatch: | |
| types: [release-tag-created] | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Tag to release (e.g., v0.1.0)' | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| packages: write | |
| actions: write | |
| jobs: | |
| prepare: | |
| name: Determine tag and verify CI | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag: ${{ steps.tag.outputs.tag }} | |
| require_ci: ${{ steps.verify_ci.outputs.require_ci }} | |
| steps: | |
| - name: Determine tag to release | |
| id: tag | |
| run: | | |
| if [ -n "${{ github.event.inputs.tag }}" ]; then | |
| TAG="${{ github.event.inputs.tag }}" | |
| elif [ -n "${{ github.event.client_payload.tag }}" ]; then | |
| TAG="${{ github.event.client_payload.tag }}" | |
| else | |
| TAG="${{ github.ref_name }}" | |
| fi | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| echo "Releasing tag: $TAG" | |
| - name: Ensure CI succeeded for tag commit | |
| id: verify_ci | |
| uses: actions/github-script@v7 | |
| env: | |
| RELEASE_TAG: ${{ steps.tag.outputs.tag }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const tag = process.env.RELEASE_TAG; | |
| if (!tag) { | |
| core.setFailed('No release tag determined.'); | |
| return; | |
| } | |
| const { data: ref } = await github.rest.git.getRef({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: `tags/${tag}`, | |
| }); | |
| const sha = ref.object.sha; | |
| core.info(`Tag ${tag} points to commit ${sha}`); | |
| const { data } = await github.rest.actions.listWorkflowRuns({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| workflow_id: 'ci.yml', | |
| head_sha: sha, | |
| per_page: 1, | |
| }); | |
| const run = data.workflow_runs[0]; | |
| let requireCi = false; | |
| if (!run) { | |
| core.warning(`No CI workflow run found for commit ${sha}. Release workflow will rerun CI.`); | |
| requireCi = true; | |
| } else if (run.conclusion !== 'success') { | |
| core.warning(`CI workflow run ${run.html_url} has conclusion ${run.conclusion}. Release workflow will rerun CI.`); | |
| requireCi = true; | |
| } else { | |
| core.info(`CI workflow run ${run.html_url} succeeded at ${run.updated_at}.`); | |
| } | |
| core.setOutput('require_ci', requireCi ? 'true' : 'false'); | |
| ci: | |
| name: Rerun CI if required | |
| needs: prepare | |
| if: needs.prepare.outputs.require_ci == 'true' | |
| uses: ./.github/workflows/ci.yml | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| publish: | |
| name: Publish to Maven Central | |
| needs: [prepare, ci] | |
| if: | | |
| needs.prepare.outputs.require_ci != 'true' || | |
| (needs.ci.result != 'failure') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| env: | |
| RELEASE_TAG: ${{ needs.prepare.outputs.tag }} | |
| steps: | |
| - name: Checkout tag | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: refs/tags/${{ env.RELEASE_TAG }} | |
| fetch-depth: 0 | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: 17 | |
| cache: sbt | |
| - name: Setup sbt | |
| uses: sbt/setup-sbt@v1 | |
| - name: Import and publish GPG key | |
| id: gpg | |
| env: | |
| PGP_SECRET: ${{ secrets.PGP_SECRET }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${PGP_SECRET:-}" ]; then | |
| echo "PGP_SECRET is not configured" | |
| exit 1 | |
| fi | |
| mkdir -p ~/.gnupg | |
| echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf | |
| echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf | |
| gpgconf --kill gpg-agent | |
| echo "$PGP_SECRET" | base64 --decode --ignore-garbage | gpg --batch --yes --import | |
| KEY_FPR=$(gpg --list-secret-keys --with-colons | awk -F: '/^fpr:/ { print $10; exit }') | |
| if [ -z "$KEY_FPR" ]; then | |
| echo "Could not determine GPG key fingerprint" | |
| exit 1 | |
| fi | |
| echo "fingerprint=$KEY_FPR" >> "$GITHUB_OUTPUT" | |
| echo "Using GPG fingerprint: $KEY_FPR" | |
| KEYSERVER="hkps://keyserver.ubuntu.com" | |
| if gpg --batch --keyserver "$KEYSERVER" --send-keys "$KEY_FPR"; then | |
| echo "Uploaded public key to $KEYSERVER" | |
| else | |
| echo "::warning::Failed to upload key to $KEYSERVER. Ensure the fingerprint $KEY_FPR is published manually." | |
| fi | |
| - name: Get version | |
| id: version | |
| run: | | |
| VERSION=$(sbt --batch --no-colors 'print version' | tail -1 | xargs) | |
| echo "Version: $VERSION" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| TAG="${RELEASE_TAG}" | |
| EXPECTED_VERSION="${TAG#v}" | |
| if [ "$VERSION" != "$EXPECTED_VERSION" ]; then | |
| echo "ERROR: Version mismatch!" | |
| echo " Tag: $TAG" | |
| echo " Expected version: $EXPECTED_VERSION" | |
| echo " Actual version: $VERSION" | |
| exit 1 | |
| fi | |
| if [[ "$VERSION" =~ SNAPSHOT ]]; then | |
| echo "ERROR: Tag release cannot have SNAPSHOT version!" | |
| echo " Tag: $TAG" | |
| echo " Version: $VERSION" | |
| exit 1 | |
| fi | |
| - name: Publish to Maven Central | |
| id: maven_publish | |
| run: | | |
| export GPG_TTY=$(tty) | |
| sbt --batch --no-colors publishSigned sonatypeBundleRelease | |
| env: | |
| PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} | |
| PGP_SECRET: ${{ secrets.PGP_SECRET }} | |
| SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} | |
| SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} | |
| CI_COMMIT_TAG: ${{ env.RELEASE_TAG }} | |
| GITHUB_REF: refs/tags/${{ env.RELEASE_TAG }} | |
| - name: Build CLI assemblies | |
| run: sbt --batch --no-colors "validation-cli/assembly" "contracts-extractor-cli/assembly" "maintenance-cli/assembly" | |
| - name: Collect artifacts | |
| run: | | |
| mkdir -p release-artifacts | |
| find modules -maxdepth 4 -type f -name "*assembly*.jar" -print -exec cp {} release-artifacts/ \; | |
| ls -la release-artifacts | |
| - name: Generate checksums | |
| run: | | |
| cd release-artifacts | |
| sha256sum -- *.jar > SHA256SUMS | |
| sha512sum -- *.jar > SHA512SUMS | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-artifacts-${{ steps.version.outputs.version }} | |
| path: release-artifacts/ | |
| retention-days: 30 | |
| - name: Create GitHub Release | |
| uses: ncipollo/release-action@v1 | |
| with: | |
| tag: ${{ env.RELEASE_TAG }} | |
| name: FlowForge ${{ steps.version.outputs.version }} | |
| body: | | |
| ## FlowForge ${{ steps.version.outputs.version }} | |
| Functional-first, type-safe data engineering framework. | |
| ### What's changed | |
| - [Changelog](https://github.com/${{github.repository}}/blob/main/CHANGELOG.md) | |
| --- | |
| ### Installation | |
| ```scala | |
| libraryDependencies += "com.flowforge" %% "flowforge-core" % "${{steps.version.outputs.version}}" | |
| ``` | |
| See [documentation](https://github.com/${{github.repository}}) for connectors and engines. | |
| artifacts: "release-artifacts/*" | |
| draft: false | |
| prerelease: false | |
| makeLatest: true | |
| allowUpdates: true | |
| updateOnlyUnreleased: false | |
| - name: Trigger Changelog update | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| await github.rest.repos.createDispatchEvent({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| event_type: 'release-completed', | |
| client_payload: { | |
| tag: process.env.RELEASE_TAG | |
| } | |
| }); | |
| - name: Release summary | |
| run: | | |
| echo "## Release Complete" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Version**: \`${{ steps.version.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Artifacts uploaded and changelog workflow triggered." >> $GITHUB_STEP_SUMMARY |