tests: improve coverage #1
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: Release | ||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| version: | ||
| description: 'Release version (e.g., 0.1.0)' | ||
| required: true | ||
| type: string | ||
| prerelease: | ||
| description: 'Mark as pre-release' | ||
| required: false | ||
| default: false | ||
| type: boolean | ||
| target: | ||
| description: 'target_commitish (branch name or commit SHA). Defaults to the triggering commit.' | ||
| required: false | ||
| type: string | ||
| inputs: | ||
| version: | ||
| description: 'Release version (e.g., 0.1.0)' | ||
| required: true | ||
| type: string | ||
| prerelease: | ||
| description: 'Mark as pre-release' | ||
| required: false | ||
| default: false | ||
| type: boolean | ||
| permissions: | ||
| contents: write | ||
| pages: write | ||
| id-token: write | ||
| concurrency: | ||
| group: release-${{ github.ref }} | ||
| cancel-in-progress: false | ||
| env: | ||
| JAVA_OPTS: -Xmx4G -XX:+UseG1GC | ||
| SBT_OPTS: -Xmx4G -XX:+UseG1GC | ||
| jobs: | ||
| validate-release: | ||
| name: Validate Release | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| version: ${{ steps.version.outputs.version }} | ||
| is-prerelease: ${{ steps.version.outputs.is-prerelease }} | ||
| target-commitish: ${{ steps.version.outputs.target_commitish }} | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Determine version | ||
| id: version | ||
| run: | | ||
| VERSION="${{ github.event.inputs.version }}" | ||
| IS_PRERELEASE="${{ github.event.inputs.prerelease }}" | ||
| TARGET_INPUT="${{ github.event.inputs.target }}" | ||
| if [ -z "$TARGET_INPUT" ]; then | ||
| TARGET_INPUT="${GITHUB_SHA}" | ||
| fi | ||
| echo "version=$VERSION" >> $GITHUB_OUTPUT | ||
| echo "is-prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT | ||
| echo "target_commitish=$TARGET_INPUT" >> $GITHUB_OUTPUT | ||
| echo "Release version: $VERSION (prerelease: $IS_PRERELEASE, target=$TARGET_INPUT)" | ||
| - name: Validate version format | ||
| run: | | ||
| VERSION="${{ steps.version.outputs.version }}" | ||
| if ! [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then | ||
| echo "Invalid version format: $VERSION" | ||
| echo "Expected format: X.Y.Z or X.Y.Z-qualifier" | ||
| exit 1 | ||
| fi | ||
| - name: Check for existing release | ||
| run: | | ||
| VERSION="${{ steps.version.outputs.version }}" | ||
| if gh release view "v$VERSION" &>/dev/null; then | ||
| echo "Release v$VERSION already exists!" | ||
| exit 1 | ||
| fi | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| pre-release-tests: | ||
| name: Pre-release tests | ||
| needs: [validate-release] | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| - name: Setup Java 17 | ||
| uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 | ||
| with: | ||
| distribution: 'temurin' | ||
| java-version: '17' | ||
| - name: Coursier cache (sbt/ivy) | ||
| uses: coursier/cache-action@4e2615869d13561d626ed48655e1a39e5b192b3c | ||
| - name: Check formatting | ||
| run: sbt scalafmtCheckAll | ||
| - name: Compile all modules | ||
| run: sbt compile Test/compile | ||
| - name: Run full test suite | ||
| run: | | ||
| echo "🧪 Running full test suite for release" | ||
| sbt coverage test | ||
| echo "🔥 Running compile-fail tests - proving contract enforcement" | ||
| sbt "compile-fail-tests / test" | ||
| echo "🚀 Running engine smoke tests" | ||
| sbt "engines-spark/testOnly *StreamingCDCSpec" | ||
| sbt "engines-flink/testOnly *EngineAbstractionSpec" | ||
| sbt coverageReport | ||
| echo "✅ All release tests passed!" | ||
| - name: Generate coverage report | ||
| run: sbt coverageAggregate | ||
| - name: Validate g8 template for release | ||
| run: | | ||
| echo "📋 Validating g8 template for release" | ||
| cd /tmp | ||
| sbt new file://${{ github.workspace }}/flowforge.g8 --name="release-test-pipeline" --organization="com.flowforge.release" | ||
| cd release-test-pipeline | ||
| echo "🔨 Testing template compilation" | ||
| sbt compile | ||
| echo "🧪 Testing template tests" | ||
| sbt test | ||
| echo "🎯 Testing contract drift demo" | ||
| sed -i 's/id: Long/id: String/' src/main/scala/Contract.scala | ||
| if sbt compile; then | ||
| echo "❌ Contract drift should have failed compilation!" | ||
| exit 1 | ||
| else | ||
| echo "✅ Contract drift prevention working correctly!" | ||
| fi | ||
| build-artifacts: | ||
| name: Build release artifacts | ||
| needs: [validate-release, pre-release-tests] | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| - name: Setup Java 17 | ||
| uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 | ||
| with: | ||
| distribution: 'temurin' | ||
| java-version: '17' | ||
| - name: Coursier cache (sbt/ivy) | ||
| uses: coursier/cache-action@4e2615869d13561d626ed48655e1a39e5b192b3c | ||
| - name: Set release version | ||
| run: | | ||
| VERSION="${{ needs.validate-release.outputs.version }}" | ||
| echo "Building with version: $VERSION (from git tag)" | ||
| # sbt-dynver uses git tags for versioning automatically | ||
| git tag -f "v$VERSION" 2>/dev/null || true | ||
| - name: Build JAR artifacts | ||
| run: | | ||
| sbt clean compile | ||
| sbt "validation-cli / assembly" | ||
| sbt "contracts-extractor-cli / assembly" | ||
| - name: Create release directory | ||
| run: mkdir -p release-artifacts | ||
| - name: Copy artifacts | ||
| run: | | ||
| find . -name "*assembly*.jar" -exec cp {} release-artifacts/ \; | ||
| ls -la release-artifacts/ | ||
| - name: Upload artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: release-artifacts-${{ needs.validate-release.outputs.version }} | ||
| path: release-artifacts/ | ||
| retention-days: 30 | ||
| publish-docs: | ||
| name: Publish Scaladoc to GitHub Pages | ||
| needs: [validate-release, pre-release-tests] | ||
| runs-on: ubuntu-latest | ||
| environment: | ||
| name: github-pages | ||
| url: ${{ steps.deployment.outputs.page_url }} | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Setup Java 17 | ||
| uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 | ||
| with: | ||
| distribution: 'temurin' | ||
| java-version: '17' | ||
| - name: Coursier cache (sbt/ivy) | ||
| uses: coursier/cache-action@4e2615869d13561d626ed48655e1a39e5b192b3c | ||
| - name: Generate Scaladoc for all modules | ||
| run: | | ||
| sbt -batch doc | ||
| # Try unified docs for Scala 2.13 modules; don't fail release if unidoc fails | ||
| sbt -batch unidoc || echo "unidoc failed (non-fatal)" | ||
| - name: Assemble site folder | ||
| run: | | ||
| mkdir -p public/api | ||
| for m in modules/*; do \ | ||
| mod=$(basename "$m"); \ | ||
| api_dir="$m/target/scala-2.13/api"; \ | ||
| if [ -d "$api_dir" ]; then \ | ||
| mkdir -p "public/api/$mod"; \ | ||
| cp -R "$api_dir"/* "public/api/$mod"/; \ | ||
| fi; \ | ||
| done | ||
| # Root module (if any aggregated docs exist) | ||
| if [ -d target/scala-2.13/api ]; then \ | ||
| mkdir -p public/api/root && cp -R target/scala-2.13/api/* public/api/root/; \ | ||
| fi | ||
| # Unified docs (if present) | ||
| if [ -d target/scala-2.13/unidoc ] || [ -d target/scala-2.13/api-unidoc ]; then \ | ||
| UNIDOC_DIR="$(ls -d target/scala-2.13/*unidoc 2>/dev/null | head -n1)"; \ | ||
| if [ -n "$UNIDOC_DIR" ]; then \ | ||
| mkdir -p public/api/unified && cp -R "$UNIDOC_DIR"/* public/api/unified/; \ | ||
| fi; \ | ||
| fi | ||
| # Add index | ||
| cat > public/index.html << 'EOF' | ||
| <!doctype html> | ||
| <html><head><meta charset="utf-8"><title>FlowForge API</title></head> | ||
| <body> | ||
| <h1>FlowForge API (Scaladoc)</h1> | ||
| <ul> | ||
| $(for d in api/*; do echo "<li><a href=\"$d/index.html\">$(basename "$d")</a></li>"; done) | ||
| </ul> | ||
| </body></html> | ||
| EOF | ||
| - name: Upload Pages artifact | ||
| uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa | ||
| with: | ||
| path: public | ||
| - name: Deploy to GitHub Pages | ||
| id: deployment | ||
| uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e | ||
| create-release: | ||
| name: Create GitHub Release | ||
| needs: [validate-release, pre-release-tests, build-artifacts, publish-docs] | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | ||
| - name: Download artifacts | ||
| uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e | ||
| with: | ||
| name: release-artifacts-${{ needs.validate-release.outputs.version }} | ||
| path: release-artifacts/ | ||
| - name: Generate dynamic release notes | ||
| id: release-notes | ||
| run: | | ||
| VERSION="${{ needs.validate-release.outputs.version }}" | ||
| PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") | ||
| { | ||
| echo "# FlowForge v$VERSION" | ||
| echo "" | ||
| if [ -n "$PREV_TAG" ]; then | ||
| echo "## 📝 Changes since $PREV_TAG" | ||
| echo "" | ||
| git log ${PREV_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges | head -n 50 | ||
| echo "" | ||
| echo "" | ||
| fi | ||
| echo "## 🚀 Core Features" | ||
| echo "- 100% Compile-time contracts - Data pipelines fail to compile on schema drift" | ||
| echo "- Phantom-state builder pattern - Type-safe pipeline construction" | ||
| echo "- Macro-based schema validation - Zero-runtime overhead" | ||
| echo "" | ||
| echo "## 🛠 CLI Tools" | ||
| echo "- **validation-cli**: Schema validation and contract checking" | ||
| echo "- **contracts-extractor-cli**: Extract contracts from data sources" | ||
| echo "" | ||
| echo "## 📊 Statistics" | ||
| COMMIT_COUNT=$(git rev-list --count ${PREV_TAG}..HEAD 2>/dev/null || echo "N/A") | ||
| CONTRIBUTOR_COUNT=$(git shortlog -sn ${PREV_TAG}..HEAD 2>/dev/null | wc -l || echo "N/A") | ||
| echo "- Commits: $COMMIT_COUNT" | ||
| echo "- Contributors: $CONTRIBUTOR_COUNT" | ||
| echo "- Commit: \`${{ github.sha }}\`" | ||
| echo "" | ||
| echo "## 📚 Documentation" | ||
| echo "- API Docs: https://vim89.github.io/flowforge/api/" | ||
| echo "- Getting Started: https://github.com/vim89/flowforge#getting-started" | ||
| } > release-notes.md | ||
| - name: Create gitHub release | ||
| uses: softprops/action-gh-release@26f96dfa697d77e81fd5907df203aa23a56210a8 | ||
| with: | ||
| tag_name: v${{ needs.validate-release.outputs.version }} | ||
| name: FlowForge v${{ needs.validate-release.outputs.version }} | ||
| body_path: release-notes.md | ||
| target_commitish: ${{ needs.validate-release.outputs.target-commitish }} | ||
| prerelease: ${{ needs.validate-release.outputs.is-prerelease == 'true' }} | ||
| files: release-artifacts/* | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Upload to Codecov | ||
| if: success() | ||
| uses: codecov/codecov-action@v5 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| fail_ci_if_error: false | ||
| - name: Rollback instructions | ||
| if: failure() | ||
| run: | | ||
| echo "❌ Release failed!" | ||
| echo "" | ||
| echo "To rollback:" | ||
| echo " 1. Delete failed release: gh release delete v${{ needs.validate-release.outputs.version }} -y" | ||
| echo " 2. Delete tag: git push --delete origin v${{ needs.validate-release.outputs.version }}" | ||
| echo " 3. Fix issues and re-tag when ready" | ||