Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 55 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,19 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

# Install both JDK 17 (for compilation) and the matrix JDK (for
# test execution). setup-java exports JAVA_HOME_<version>_<arch>;
# Gradle's toolchain auto-detection picks them up.
- name: Set up JDKs (compile=17, test=${{ matrix.java }})
# Install both the matrix JDK (test execution) and JDK 17 (compile +
# Gradle daemon runtime). Order matters: setup-java sets JAVA_HOME
# to the LAST entry, and Gradle 8.12 only supports JDKs up to 23 as
# its own runtime — so JDK 17 must be last for matrix.java = 25.
# setup-java still exports JAVA_HOME_<version>_<arch> for both, and
# setup-gradle registers them as toolchain candidates.
- name: Set up JDKs (test=${{ matrix.java }}, compile/daemon=17)
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: |
17
${{ matrix.java }}
17

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
Expand Down Expand Up @@ -72,8 +75,50 @@ jobs:
files: build/reports/jacoco/test/jacocoTestReport.xml
fail_ci_if_error: true

# Integration-tests job is intentionally not wired up yet:
# SDK requirements §13 says they run on PRs and release pipelines, but
# they hit the live API and require a MARKETDATA_TOKEN secret. Add this
# job (gated on `if: ${{ secrets.MARKETDATA_TOKEN != '' }}`) once the
# token is configured in the repo's GitHub Actions secrets.
# SDK requirements §13: integration tests run mandatorily on every push
# to main (release-pipeline gate) on the full forward-compat matrix.
# On PRs they're triggered on demand instead — see
# pr-integration-on-demand.yml.
integration-tests:
name: Integration tests (live API, JDK ${{ matrix.java }})
runs-on: ubuntu-latest
needs: verify
strategy:
fail-fast: false
matrix:
java: ['17', '21', '25']
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDKs (test=${{ matrix.java }}, compile/daemon=17)
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: |
${{ matrix.java }}
17

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4

- name: Run integration tests against live API
env:
MARKETDATA_TOKEN: ${{ secrets.MARKETDATA_TOKEN }}
MARKETDATA_RUN_INTEGRATION_TESTS: 'true'
run: |
if [ -z "$MARKETDATA_TOKEN" ]; then
echo "::error::MARKETDATA_TOKEN secret is required on main; integration tests must run on every merge."
exit 1
fi
./gradlew integrationTest -PtestJdk=${{ matrix.java }} --stacktrace

- name: Upload integration-test reports on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: integration-test-reports-jdk${{ matrix.java }}
path: |
build/reports/tests/integrationTest/
build/test-results/integrationTest/
retention-days: 14
205 changes: 205 additions & 0 deletions .github/workflows/pr-integration-on-demand.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
name: Integration tests on demand

# Manually triggered by commenting on an open PR:
# `integrationtest` → JDK 17 only
# `integrationtestfull` → full matrix {17, 21, 25}
#
# Integration tests hit the live Market Data API, so we don't run them
# automatically on every PR open/sync (saves API quota + CI minutes).
# They ARE required for merge — branch protection on `main` should list
# "Integration tests pass" as a required status check, which is the
# aggregator job below. PRs cannot merge until a reviewer comments one
# of the two trigger phrases AND the resulting run is green.
#
# Important security note: workflows triggered by `issue_comment` always
# run from the *default branch's* version of the workflow file, not from
# the PR. Adding/changing this file on a feature branch has no effect
# until it lands on main.
on:
issue_comment:
types: [created]

permissions:
contents: read
pull-requests: write # to react and post the result comment

# Multiple trigger comments on the same PR cancel earlier runs.
concurrency:
group: pr-integration-on-demand-${{ github.event.issue.number }}
cancel-in-progress: true

jobs:
guard:
name: Guard
runs-on: ubuntu-latest
# Only fire on PR comments (not generic issue comments) that contain
# one of the two accepted slash-style commands. `contains` is
# substring match — note that `integrationtest` is itself a substring
# of `integrationtestfull`, so this OR matches both, and the matrix
# decision below disambiguates.
if: |
github.event.issue.pull_request != null && (
contains(github.event.comment.body, 'integrationtest') ||
contains(github.event.comment.body, 'integrationtestfull')
)
outputs:
head_sha: ${{ steps.pr.outputs.head_sha }}
jdks: ${{ steps.matrix.outputs.jdks }}
mode: ${{ steps.matrix.outputs.mode }}
steps:
- name: Verify commenter has write permission
uses: actions/github-script@v7
with:
script: |
const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: context.payload.comment.user.login,
});
const allowed = ['write', 'maintain', 'admin'].includes(perm.permission);
if (!allowed) {
core.setFailed(
`@${context.payload.comment.user.login} (${perm.permission}) ` +
`cannot trigger integration tests; write access required.`
);
}

- name: React 👀 to the trigger comment
uses: actions/github-script@v7
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'eyes',
});

- name: Resolve PR head SHA
id: pr
uses: actions/github-script@v7
with:
script: |
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.issue.number,
});
if (pr.state !== 'open') {
core.setFailed(`PR #${pr.number} is ${pr.state}; refusing to run.`);
return;
}
core.setOutput('head_sha', pr.head.sha);

- name: Decide JDK matrix from comment body
id: matrix
env:
BODY: ${{ github.event.comment.body }}
run: |
# Check for the long form first because it contains 'integrationtest' as a substring.
if [[ "$BODY" == *"integrationtestfull"* ]]; then
echo 'jdks=["17","21","25"]' >> "$GITHUB_OUTPUT"
echo 'mode=full' >> "$GITHUB_OUTPUT"
echo "Trigger: integrationtestfull → matrix {17, 21, 25}"
else
echo 'jdks=["17"]' >> "$GITHUB_OUTPUT"
echo 'mode=single' >> "$GITHUB_OUTPUT"
echo "Trigger: integrationtest → JDK 17"
fi

integration-tests:
name: Integration tests (JDK ${{ matrix.java }})
needs: guard
runs-on: ubuntu-latest
strategy:
# Don't cancel siblings: if JDK 21 fails, we still want 17 and 25
# results to surface.
fail-fast: false
matrix:
java: ${{ fromJSON(needs.guard.outputs.jdks) }}

steps:
# Check out exactly the PR's HEAD commit so we test the proposed
# change, not the merge ref.
- name: Checkout PR head
uses: actions/checkout@v4
with:
ref: ${{ needs.guard.outputs.head_sha }}

# Order matters: JDK 17 must be last so JAVA_HOME=17 (Gradle 8.12
# only supports JDKs up to 23 as its daemon runtime). The matrix
# JDK is still installed and registered as a toolchain target.
- name: Set up JDKs (test=${{ matrix.java }}, compile/daemon=17)
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: |
${{ matrix.java }}
17

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4

- name: Run integration tests against live API
env:
MARKETDATA_TOKEN: ${{ secrets.MARKETDATA_TOKEN }}
MARKETDATA_RUN_INTEGRATION_TESTS: 'true'
run: |
if [ -z "$MARKETDATA_TOKEN" ]; then
echo "::error::MARKETDATA_TOKEN secret missing — cannot run integration tests."
exit 1
fi
./gradlew integrationTest -PtestJdk=${{ matrix.java }} --stacktrace

- name: Upload integration-test reports on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: integration-test-reports-jdk${{ matrix.java }}
path: |
build/reports/tests/integrationTest/
build/test-results/integrationTest/
retention-days: 14

# Aggregator job. Branch protection on `main` should require this
# check name ("Integration tests pass") so a single required check
# covers both `integrationtest` (matrix=[17]) and `integrationtestfull`
# (matrix=[17,21,25]) modes uniformly. Without this, branch protection
# would have to list the per-matrix-entry check names which only exist
# in the `full` mode.
required-check:
name: Integration tests pass
needs: [guard, integration-tests]
if: always() && needs.guard.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Aggregate matrix outcome
env:
MATRIX_RESULT: ${{ needs.integration-tests.result }}
MODE: ${{ needs.guard.outputs.mode }}
run: |
echo "Mode: $MODE"
echo "Matrix outcome: $MATRIX_RESULT"
if [[ "$MATRIX_RESULT" != "success" ]]; then
echo "::error::One or more integration-test JDK entries failed."
exit 1
fi
echo "All integration tests passed."

- name: Comment outcome on the PR
if: always()
uses: actions/github-script@v7
with:
script: |
const ok = '${{ needs.integration-tests.result }}' === 'success';
const mode = '${{ needs.guard.outputs.mode }}';
const emoji = ok ? '✅' : '❌';
const status = ok ? 'passed' : 'failed';
const matrix = mode === 'full' ? '`{17, 21, 25}`' : '`17`';
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
body: `${emoji} On-demand integration tests on JDK ${matrix} ${status}. [View run](${runUrl}).`,
});
8 changes: 5 additions & 3 deletions .github/workflows/pr-matrix-on-demand.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,16 @@ jobs:
with:
ref: ${{ needs.guard.outputs.head_sha }}

# Compile=17, test=matrix JDK; same shape as main.yml.
- name: Set up JDKs (compile=17, test=${{ matrix.java }})
# Order matters: JDK 17 must be last so JAVA_HOME=17 (Gradle 8.12
# doesn't support JDK 24+ as its daemon runtime). The matrix JDK
# is still installed and registered as a toolchain target.
- name: Set up JDKs (test=${{ matrix.java }}, compile/daemon=17)
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: |
17
${{ matrix.java }}
17

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,10 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
files: build/reports/jacoco/test/jacocoTestReport.xml
fail_ci_if_error: true

# NOTE: integration tests are NOT run automatically on PR open/sync.
# They are required for merge but only fire when a reviewer comments
# `integrationtest` (JDK 17) or `integrationtestfull` (matrix 17/21/25)
# — see .github/workflows/pr-integration-on-demand.yml. Branch-protection
# rules should require the "Integration tests pass" check name produced
# by that workflow before allowing merge to main.
Loading
Loading