Skip to content

SK-3002 release/26.8.1 #98

SK-3002 release/26.8.1

SK-3002 release/26.8.1 #98

name: Contract Tests
on:
pull_request:
branches:
- main
- release/*
- flowvault-release/*
jobs:
contract-tests:
# One job per module so a break in one is reported against that module by name,
# and both still run even when the other fails.
name: Contract Tests (${{ matrix.module }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- module: skyvault
artifact: skyflow-java
- module: flowvault
artifact: skyflow-flowvault-java
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '11'
cache: 'maven'
- name: Verify API surface snapshot
run: mvn -B install -pl common,${{ matrix.module }} -am -DskipTests -Dmaven.javadoc.skip=true -Dgpg.skip=true
- name: Show API surface diff
if: failure()
run: |
echo "### API surface changes detected in ${{ matrix.module }} ###"
echo "Compared against ${{ matrix.module }}/api-report/${{ matrix.artifact }}.baseline.jar."
echo "If this change is intentional, run:"
echo " scripts/contract-snapshot-update.sh ${{ matrix.module }}"
echo "and commit the updated baseline jar."
echo ""
cat ${{ matrix.module }}/target/japicmp/default-cli.diff || true
- name: Upload API surface diff on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: api-surface-diff-${{ matrix.module }}
path: ${{ matrix.module }}/target/japicmp/**
retention-days: 7
# The step above only shows a diff when the CURRENT build differs from the
# committed baseline - once someone runs contract-snapshot-update.sh and
# commits the refreshed baseline jar, that check goes green and shows nothing.
# A reviewer looking at a green PR that touches api-report/*.baseline.jar
# (a binary file) would otherwise have no way to see WHAT was just approved as
# the new contract. These steps explicitly diff the OLD committed baseline
# (from the PR's base branch) against the NEW committed baseline (from this PR)
# and post it as a PR comment, regardless of whether the check above passed.
- name: Check if contract baseline was updated in this PR
id: baseline-diff-check
if: always() && github.event.pull_request
run: |
git fetch origin "${{ github.event.pull_request.base.ref }}" --depth=1
BASELINE="${{ matrix.module }}/api-report/${{ matrix.artifact }}.baseline.jar"
if ! git diff --name-only "origin/${{ github.event.pull_request.base.ref }}" HEAD -- "$BASELINE" | grep -q .; then
echo "changed=false" >> "$GITHUB_OUTPUT"
elif git cat-file -e "origin/${{ github.event.pull_request.base.ref }}:$BASELINE" 2>/dev/null; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
# Added by this PR rather than modified: the module is getting its
# first baseline. git diff reports an addition as a change, but there
# is no old snapshot to `git show`, so a plain "true" here would send
# the next step into `git show <base>:<path>` and exit 128.
echo "changed=new" >> "$GITHUB_OUTPUT"
fi
- name: Diff old vs new contract baseline
if: always() && (steps.baseline-diff-check.outputs.changed == 'true' || steps.baseline-diff-check.outputs.changed == 'new')
run: |
BASELINE="${{ matrix.module }}/api-report/${{ matrix.artifact }}.baseline.jar"
if [ "${{ steps.baseline-diff-check.outputs.changed }}" = "new" ]; then
{
echo "\`$BASELINE\` is **new in this PR** - \`${{ matrix.module }}\` had no committed baseline before, so there is nothing to diff against."
echo ""
echo "This snapshot becomes the approved contract: every later PR is compared against it, and any incompatible change fails the \`Contract Tests (${{ matrix.module }})\` job until someone regenerates it deliberately. Review it as the starting point, not as a change."
} > /tmp/contract-baseline-diff.md
cat /tmp/contract-baseline-diff.md
exit 0
fi
curl -sL -o /tmp/japicmp-cli.jar "https://repo.maven.apache.org/maven2/com/github/siom79/japicmp/japicmp/0.26.0/japicmp-0.26.0-jar-with-dependencies.jar"
mvn -q -B dependency:build-classpath -pl ${{ matrix.module }} -Dmdep.outputFile=/tmp/module-classpath.txt -Dmaven.javadoc.skip=true -Dgpg.skip=true
git show "origin/${{ github.event.pull_request.base.ref }}:$BASELINE" > /tmp/old-baseline.jar
# Same allowlist the poms gate on, so the comment shows the contract and
# nothing else. Keep these in sync with the <includes> in the module poms.
java -jar /tmp/japicmp-cli.jar \
-o /tmp/old-baseline.jar \
-n "$BASELINE" \
-a protected \
-i "com.skyflow.Skyflow;com.skyflow.config;com.skyflow.enums;com.skyflow.errors;com.skyflow.serviceaccount.util;com.skyflow.vault.audit;com.skyflow.vault.bin;com.skyflow.vault.connection;com.skyflow.vault.controller;com.skyflow.vault.data;com.skyflow.vault.detect;com.skyflow.vault.tokens" \
--old-classpath "$(cat /tmp/module-classpath.txt)" \
--new-classpath "$(cat /tmp/module-classpath.txt)" \
-m \
--ignore-missing-classes \
--markdown > /tmp/contract-baseline-diff.md || true
cat /tmp/contract-baseline-diff.md
- name: Comment contract baseline change on PR
if: always() && (steps.baseline-diff-check.outputs.changed == 'true' || steps.baseline-diff-check.outputs.changed == 'new')
uses: actions/github-script@v7
env:
BASELINE_STATE: ${{ steps.baseline-diff-check.outputs.changed }}
with:
script: |
const fs = require('fs');
const module = '${{ matrix.module }}';
const artifact = '${{ matrix.artifact }}';
const summary = fs.readFileSync('/tmp/contract-baseline-diff.md', 'utf8');
// per-module marker so the two matrix jobs update their own comment
const marker = `<!-- contract-baseline-diff:${module} -->`;
const isNew = process.env.BASELINE_STATE === 'new';
const heading = isNew
? `## Contract baseline added (\`${module}\`)`
: `## Contract baseline change detected (\`${module}\`)`;
const preamble = isNew
? `This PR adds \`${module}/api-report/${artifact}.baseline.jar\`, the approved public API contract for this module.`
: `This PR updates \`${module}/api-report/${artifact}.baseline.jar\` (the approved public API contract). Here is exactly what it changes, comparing the baseline on \`${{ github.event.pull_request.base.ref }}\` against the baseline committed in this PR:`;
const body = `${marker}\n${heading}\n\n${preamble}\n\n${summary}`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c => c.body && c.body.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}