55 branches :
66 - main
77 - release/*
8+ - flowvault-release/*
89
910jobs :
1011 contract-tests :
11- name : Contract Tests
12+ # One job per module so a break in one is reported against that module by name,
13+ # and both still run even when the other fails.
14+ name : Contract Tests (${{ matrix.module }})
1215 runs-on : ubuntu-latest
1316
17+ strategy :
18+ fail-fast : false
19+ matrix :
20+ include :
21+ - module : skyvault
22+ artifact : skyflow-java
23+ - module : flowvault
24+ artifact : skyflow-flowvault-java
25+
1426 permissions :
1527 contents : read
1628 pull-requests : write
@@ -29,29 +41,31 @@ jobs:
2941 cache : ' maven'
3042
3143 - name : Verify API surface snapshot
32- run : mvn -B install -pl common,skyvault -am -DskipTests -Dmaven.javadoc.skip=true -Dgpg.skip=true
44+ run : mvn -B install -pl common,${{ matrix.module }} -am -DskipTests -Dmaven.javadoc.skip=true -Dgpg.skip=true
3345
3446 - name : Show API surface diff
3547 if : failure()
3648 run : |
37- echo "### API surface changes detected ###"
38- echo "See skyvault/target/japicmp/default-cli.diff for the full comparison against skyvault/api-report/skyflow-java.baseline.jar."
39- echo "If this change is intentional, run scripts/contract-snapshot-update.sh and commit the updated baseline jar."
49+ echo "### API surface changes detected in ${{ matrix.module }} ###"
50+ echo "Compared against ${{ matrix.module }}/api-report/${{ matrix.artifact }}.baseline.jar."
51+ echo "If this change is intentional, run:"
52+ echo " scripts/contract-snapshot-update.sh ${{ matrix.module }}"
53+ echo "and commit the updated baseline jar."
4054 echo ""
41- cat skyvault /target/japicmp/default-cli.diff || true
55+ cat ${{ matrix.module }} /target/japicmp/default-cli.diff || true
4256
4357 - name : Upload API surface diff on failure
4458 if : failure()
4559 uses : actions/upload-artifact@v4
4660 with :
47- name : api-surface-diff
48- path : skyvault /target/japicmp/**
61+ name : api-surface-diff-${{ matrix.module }}
62+ path : ${{ matrix.module }} /target/japicmp/**
4963 retention-days : 7
5064
5165 # The step above only shows a diff when the CURRENT build differs from the
5266 # committed baseline - once someone runs contract-snapshot-update.sh and
5367 # commits the refreshed baseline jar, that check goes green and shows nothing.
54- # A reviewer looking at a green PR that touches api-report/skyflow-java .baseline.jar
68+ # A reviewer looking at a green PR that touches api-report/* .baseline.jar
5569 # (a binary file) would otherwise have no way to see WHAT was just approved as
5670 # the new contract. These steps explicitly diff the OLD committed baseline
5771 # (from the PR's base branch) against the NEW committed baseline (from this PR)
@@ -61,40 +75,74 @@ jobs:
6175 if : always() && github.event.pull_request
6276 run : |
6377 git fetch origin "${{ github.event.pull_request.base.ref }}" --depth=1
64- if git diff --name-only "origin/${{ github.event.pull_request.base.ref }}" HEAD -- skyvault/api-report/skyflow-java.baseline.jar | grep -q .; then
78+ BASELINE="${{ matrix.module }}/api-report/${{ matrix.artifact }}.baseline.jar"
79+ if ! git diff --name-only "origin/${{ github.event.pull_request.base.ref }}" HEAD -- "$BASELINE" | grep -q .; then
80+ echo "changed=false" >> "$GITHUB_OUTPUT"
81+ elif git cat-file -e "origin/${{ github.event.pull_request.base.ref }}:$BASELINE" 2>/dev/null; then
6582 echo "changed=true" >> "$GITHUB_OUTPUT"
6683 else
67- echo "changed=false" >> "$GITHUB_OUTPUT"
84+ # Added by this PR rather than modified: the module is getting its
85+ # first baseline. git diff reports an addition as a change, but there
86+ # is no old snapshot to `git show`, so a plain "true" here would send
87+ # the next step into `git show <base>:<path>` and exit 128.
88+ echo "changed=new" >> "$GITHUB_OUTPUT"
6889 fi
6990
7091 - name : Diff old vs new contract baseline
71- if : always() && steps.baseline-diff-check.outputs.changed == 'true'
92+ if : always() && ( steps.baseline-diff-check.outputs.changed == 'true' || steps.baseline-diff-check.outputs.changed == 'new')
7293 run : |
94+ BASELINE="${{ matrix.module }}/api-report/${{ matrix.artifact }}.baseline.jar"
95+
96+ if [ "${{ steps.baseline-diff-check.outputs.changed }}" = "new" ]; then
97+ {
98+ echo "\`$BASELINE\` is **new in this PR** - \`${{ matrix.module }}\` had no committed baseline before, so there is nothing to diff against."
99+ echo ""
100+ 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."
101+ } > /tmp/contract-baseline-diff.md
102+ cat /tmp/contract-baseline-diff.md
103+ exit 0
104+ fi
105+
73106 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"
74- mvn -q -B dependency:build-classpath -pl skyvault -Dmdep.outputFile=/tmp/skyvault -classpath.txt -Dmaven.javadoc.skip=true -Dgpg.skip=true
75- git show "origin/${{ github.event.pull_request.base.ref }}:skyvault/api-report/skyflow-java.baseline.jar " > /tmp/old-baseline.jar
107+ mvn -q -B dependency:build-classpath -pl ${{ matrix.module }} -Dmdep.outputFile=/tmp/module -classpath.txt -Dmaven.javadoc.skip=true -Dgpg.skip=true
108+ git show "origin/${{ github.event.pull_request.base.ref }}:$BASELINE " > /tmp/old-baseline.jar
76109
110+ # Same allowlist the poms gate on, so the comment shows the contract and
111+ # nothing else. Keep these in sync with the <includes> in the module poms.
77112 java -jar /tmp/japicmp-cli.jar \
78113 -o /tmp/old-baseline.jar \
79- -n skyvault/api-report/skyflow-java.baseline.jar \
114+ -n "$BASELINE" \
80115 -a protected \
81- -e "com.skyflow.generated.* ;com.skyflow.utils.* " \
82- --old-classpath "$(cat /tmp/skyvault -classpath.txt)" \
83- --new-classpath "$(cat /tmp/skyvault -classpath.txt)" \
116+ -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 " \
117+ --old-classpath "$(cat /tmp/module -classpath.txt)" \
118+ --new-classpath "$(cat /tmp/module -classpath.txt)" \
84119 -m \
120+ --ignore-missing-classes \
85121 --markdown > /tmp/contract-baseline-diff.md || true
86122
87123 cat /tmp/contract-baseline-diff.md
88124
89125 - name : Comment contract baseline change on PR
90- if : always() && steps.baseline-diff-check.outputs.changed == 'true'
126+ if : always() && ( steps.baseline-diff-check.outputs.changed == 'true' || steps.baseline-diff-check.outputs.changed == 'new')
91127 uses : actions/github-script@v7
128+ env :
129+ BASELINE_STATE : ${{ steps.baseline-diff-check.outputs.changed }}
92130 with :
93131 script : |
94132 const fs = require('fs');
133+ const module = '${{ matrix.module }}';
134+ const artifact = '${{ matrix.artifact }}';
95135 const summary = fs.readFileSync('/tmp/contract-baseline-diff.md', 'utf8');
96- const marker = '<!-- contract-baseline-diff -->';
97- const body = `${marker}\n## Contract baseline change detected\n\nThis PR updates \`skyvault/api-report/skyflow-java.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:\n\n${summary}`;
136+ // per-module marker so the two matrix jobs update their own comment
137+ const marker = `<!-- contract-baseline-diff:${module} -->`;
138+ const isNew = process.env.BASELINE_STATE === 'new';
139+ const heading = isNew
140+ ? `## Contract baseline added (\`${module}\`)`
141+ : `## Contract baseline change detected (\`${module}\`)`;
142+ const preamble = isNew
143+ ? `This PR adds \`${module}/api-report/${artifact}.baseline.jar\`, the approved public API contract for this module.`
144+ : `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:`;
145+ const body = `${marker}\n${heading}\n\n${preamble}\n\n${summary}`;
98146 const { data: comments } = await github.rest.issues.listComments({
99147 owner: context.repo.owner,
100148 repo: context.repo.repo,
0 commit comments