Skip to content

Handle base64 whitespace when importing PGP key #21

Handle base64 whitespace when importing PGP key

Handle base64 whitespace when importing PGP key #21

Workflow file for this run

# 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