-
Notifications
You must be signed in to change notification settings - Fork 0
initial Market Data Java SDK scaffold + CI #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
8f2e631
first functional setup && and working project
MarketDataDev03 c770874
gh actions added
MarketDataDev03 b9aefa6
codecov added
MarketDataDev03 9b1bf3e
Test JDK 21, 25 on demand on CI
MarketDataDev03 44ee380
Change Configuration.java to object instance & correct README
MarketDataDev03 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| name: Main | ||
|
|
||
| # Runs only on push to main (i.e. when a PR is merged or someone pushes | ||
| # directly). This is where the full forward-compat JDK matrix runs and | ||
| # where we publish the canonical coverage snapshot that Codecov uses as | ||
| # the base for PR diffs. | ||
| on: | ||
| push: | ||
| branches: ['main'] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| # Don't cancel main runs against each other — we want every merge to | ||
| # produce a coverage baseline. Sequential is fine; main pushes are rare. | ||
| concurrency: | ||
| group: main | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| verify: | ||
| name: Verify (JDK ${{ matrix.java }}) | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| # Don't cancel siblings: if JDK 21 fails, we still want to know | ||
| # whether 17 and 25 pass. | ||
| fail-fast: false | ||
| matrix: | ||
| # ADR-002: tests run on JDK 17, 21, 25 to catch forward-compat | ||
| # regressions. Compilation is always pinned to --release 17. | ||
| java: ['17', '21', '25'] | ||
|
|
||
| steps: | ||
| - 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 }}) | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| distribution: temurin | ||
| java-version: | | ||
| 17 | ||
| ${{ matrix.java }} | ||
|
|
||
| - name: Set up Gradle | ||
| uses: gradle/actions/setup-gradle@v4 | ||
|
|
||
| - name: Build, test, lint, coverage | ||
| run: ./gradlew build -PtestJdk=${{ matrix.java }} --stacktrace | ||
|
|
||
| - name: Upload test reports on failure | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: test-reports-jdk${{ matrix.java }} | ||
| path: | | ||
| build/reports/tests/ | ||
| build/test-results/ | ||
| retention-days: 14 | ||
|
|
||
| # The JDK 17 entry of the matrix is the canonical run for coverage: | ||
| # its JaCoCo XML is uploaded to Codecov and becomes the base that | ||
| # subsequent PR runs compare against (see codecov.yml). | ||
| - name: Upload coverage to Codecov (JDK 17 only) | ||
| if: success() && matrix.java == '17' | ||
| uses: codecov/codecov-action@v5 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| 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. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| name: PR matrix (on demand) | ||
|
|
||
| # Manually triggered by commenting one of these slash commands on an | ||
| # open PR: | ||
| # /run-all-jdks | ||
| # /jdk-matrix | ||
| # /test-all | ||
| # Runs the forward-compat matrix (JDK 21, 25) — JDK 17 already runs | ||
| # automatically on every PR open/sync via pull-request.yml. | ||
| # | ||
| # Important security note: workflows triggered by `issue_comment` always | ||
| # run from the *default branch's* version of the workflow file, not from | ||
| # the PR. So 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 to the trigger comment | ||
|
|
||
| # Multiple "run all versions" comments on the same PR cancel earlier runs. | ||
| concurrency: | ||
| group: pr-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 three accepted slash commands. `contains` is substring | ||
| # match — false positives are possible but unlikely in practice given | ||
| # the leading slash and hyphen-rich shape of these tokens. | ||
| if: | | ||
| github.event.issue.pull_request != null && ( | ||
| contains(github.event.comment.body, '/run-all-jdks') || | ||
| contains(github.event.comment.body, '/jdk-matrix') || | ||
| contains(github.event.comment.body, '/test-all') | ||
| ) | ||
| outputs: | ||
| head_sha: ${{ steps.pr.outputs.head_sha }} | ||
| steps: | ||
| # Reject comments from anyone without write access. Otherwise an | ||
| # external user commenting on a fork PR could burn our CI minutes | ||
| # and potentially exfiltrate secrets via a malicious build. | ||
| - 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 CI; write access required.` | ||
| ); | ||
| } | ||
|
|
||
| # Visible feedback to the commenter that we picked up the trigger. | ||
| - 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', | ||
| }); | ||
|
|
||
| # The issue_comment event payload doesn't include the PR's head SHA, | ||
| # so look it up via the pulls API. We also confirm the PR is open; | ||
| # firing on closed PRs is almost always a mistake. | ||
| - 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); | ||
|
|
||
| verify: | ||
| name: Verify (JDK ${{ matrix.java }}) | ||
| needs: guard | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| # If JDK 21 fails, we still want to know whether 25 passes. | ||
| fail-fast: false | ||
| matrix: | ||
| java: ['21', '25'] | ||
|
|
||
| steps: | ||
| # Check out exactly the PR's HEAD commit, not the merge ref. | ||
| - name: Checkout PR head | ||
| uses: actions/checkout@v4 | ||
| 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 }}) | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| distribution: temurin | ||
| java-version: | | ||
| 17 | ||
| ${{ matrix.java }} | ||
|
|
||
| - name: Set up Gradle | ||
| uses: gradle/actions/setup-gradle@v4 | ||
|
|
||
| - name: Test on JDK ${{ matrix.java }} | ||
| run: ./gradlew test -PtestJdk=${{ matrix.java }} --stacktrace | ||
|
|
||
| - name: Upload test reports on failure | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: test-reports-jdk${{ matrix.java }} | ||
| path: | | ||
| build/reports/tests/ | ||
| build/test-results/ | ||
| retention-days: 14 | ||
|
|
||
| # Post a single comment summarizing the on-demand matrix result so it's | ||
| # visible on the PR without diving into the Actions tab. | ||
| report: | ||
| name: Report | ||
| needs: verify | ||
| if: always() && needs.guard.result == 'success' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Comment outcome | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const ok = '${{ needs.verify.result }}' === 'success'; | ||
| const emoji = ok ? '✅' : '❌'; | ||
| const status = ok ? 'passed' : 'failed'; | ||
| 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 JDK matrix \`{21, 25}\` ${status}. [View run](${runUrl}).`, | ||
| }); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| name: Pull Request | ||
|
|
||
| # Triggers only on pull request lifecycle events: | ||
| # - opened (PR creation) | ||
| # - synchronize (push to the PR branch while the PR is open) | ||
| # - reopened | ||
| # These are the default `pull_request` activity types — listed explicitly | ||
| # here for clarity. Pre-PR pushes don't run CI by design (saves minutes | ||
| # during early WIP commits). | ||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened] | ||
| branches: ['**'] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| # Cancel an in-progress run when a new commit lands on the same PR. | ||
| concurrency: | ||
| group: pr-${{ github.event.pull_request.number }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| verify: | ||
| name: Verify (JDK 17) | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| # PRs only run on JDK 17 (the minimum target). Forward-compat | ||
| # regressions on JDK 21/25 are caught post-merge by main.yml. | ||
| - name: Set up JDK 17 | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| distribution: temurin | ||
| java-version: '17' | ||
|
|
||
| # Validates the wrapper jar hash and caches Gradle home + wrapper | ||
| # dists between runs. | ||
| - name: Set up Gradle | ||
| uses: gradle/actions/setup-gradle@v4 | ||
|
|
||
| - name: Build, test, lint, coverage | ||
| run: ./gradlew build --stacktrace | ||
|
|
||
| - name: Upload test reports on failure | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: test-reports | ||
| path: | | ||
| build/reports/tests/ | ||
| build/test-results/ | ||
| retention-days: 14 | ||
|
|
||
| # Coverage ratchet lives in Codecov: codecov.yml at the repo root | ||
| # configures `threshold: 5%` so a PR fails the Codecov status check | ||
| # if line coverage drops more than 5 pp vs the base branch. | ||
| - name: Upload coverage to Codecov | ||
| uses: codecov/codecov-action@v5 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| files: build/reports/jacoco/test/jacocoTestReport.xml | ||
| fail_ci_if_error: true |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # Gradle | ||
| .gradle/ | ||
| build/ | ||
| !gradle/wrapper/gradle-wrapper.jar | ||
|
|
||
| # IDE — IntelliJ | ||
| .idea/ | ||
| *.iml | ||
| *.ipr | ||
| *.iws | ||
| out/ | ||
|
|
||
| # IDE — Eclipse / VS Code | ||
| .classpath | ||
| .project | ||
| .settings/ | ||
| bin/ | ||
| .vscode/ | ||
|
|
||
| # OS | ||
| .DS_Store | ||
| Thumbs.db | ||
|
|
||
| # Local env | ||
| .env | ||
| .env.local | ||
|
|
||
| # Logs / coverage | ||
| *.log | ||
| hs_err_pid* | ||
| replay_pid* | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # Changelog | ||
|
|
||
| All notable changes to this project will be documented in this file. | ||
|
|
||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), | ||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
|
|
||
| ## [Unreleased] | ||
|
|
||
| ### Added | ||
| - Project scaffold per ADRs 001–006: Gradle Kotlin DSL build, JDK 17 toolchain, | ||
| `integrationTest` source set, Spotless + JaCoCo, Vanniktech Maven Publish. | ||
| - `MarketDataClient` skeleton with builder, default base URL | ||
| (`https://api.marketdata.app`), default API version (`v1`), 99 s request / | ||
| 2 s connect timeouts, HTTP/2, demo mode, `validateOnStartup` toggle, and a | ||
| 50-permit concurrency semaphore (wiring lands with the request layer). | ||
| - Configuration cascade: explicit builder values → `MARKETDATA_*` environment | ||
| variables → `.env` file in CWD → built-in defaults. | ||
| - Sealed `MarketDataException` hierarchy with the seven canonical subtypes | ||
| (`AuthenticationError`, `BadRequestError`, `NotFoundError`, `RateLimitError`, | ||
| `ServerError`, `NetworkError`, `ParseError`), each carrying support context | ||
| (`requestId`, `requestUrl`, `statusCode`, `timestamp`) and a | ||
| `getSupportInfo()` helper. | ||
| - `RateLimits` record exposed via `MarketDataClient.getRateLimits()`. | ||
| - JSpecify `@NullMarked` on every public package; JSpecify on `compileOnlyApi` | ||
| so consumers get the annotations at compile time without a runtime dep. | ||
| - Token redaction utility (`internal.Tokens`) for log output. | ||
| - MIT license; SDK version auto-detected from the JAR manifest | ||
| (`Implementation-Version`). |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on README says this file is not commited and this is a rule with "!" to not exclude it, so the file was commited.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. The idea is to force it to always be committed (the standard convention in Gradle projects). The README is the one lying: it has an old paragraph from when the wrapper jar hadn't been generated yet, and I never updated it after committing. I'll fix it now.