Skip to content

Commit fa0810b

Browse files
committed
build: add consumer smoke verification and security policy
1 parent 910ad75 commit fa0810b

8 files changed

Lines changed: 259 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Consumer Smoke
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches: [ "main" ]
7+
pull_request:
8+
9+
concurrency:
10+
group: consumer-smoke-${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
smoke:
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v6.0.2
23+
with:
24+
fetch-depth: 0
25+
26+
- name: Setup JDK
27+
uses: actions/setup-java@v5.2.0
28+
with:
29+
distribution: temurin
30+
java-version: '17'
31+
cache: maven
32+
33+
- name: Setup Gradle
34+
uses: gradle/actions/setup-gradle@v5.0.1
35+
36+
- name: Consumer Smoke Verification
37+
run: ./scripts/verify-consumer-smoke.sh

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@ All notable changes to this project are documented in this file.
2222
- Added library integration/e2e scenarios that validate all primary registration styles (`@JsonRpcMethod`, explicit `JsonRpcMethodRegistration`, and `JsonRpcTypedMethodHandlerFactory`) with class/record/collection parameter and return payloads.
2323
- Added pure Java (no Spring) integration/e2e tests in `jsonrpc-core` and standalone usage documentation in README.
2424
- Expanded metrics coverage with transport/batch/notification instrumentation and configurable latency histogram/percentiles/method-tag cardinality limits.
25+
- Added `SECURITY.md` with vulnerability reporting policy.
26+
- Added consumer smoke automation (`scripts/verify-consumer-smoke.sh` and `.github/workflows/consumer-smoke.yml`) to verify published artifacts from fresh Maven/Gradle consumer projects.
2527

2628
### Changed
2729
- Rewrote `README.md` as a documentation entrypoint and expanded official-style documentation under `docs/` (getting started, architecture, protocol compliance, configuration reference, extension points, testing, performance, troubleshooting).
2830
- WebMVC endpoint now emits observer callbacks for parse errors, payload-size violations, batch aggregation, and notification-only calls.
2931
- Reduced runtime metrics overhead by caching Micrometer counters and replacing stack-trace scans with typed failure markers for access-control/interceptor paths.
3032
- Expanded JMH dispatcher benchmark scenarios to cover success, invalid request/params, method-not-found, and large batch profiles.
3133
- Added `:jsonrpc-core:jmhQuick` task for short JMH smoke profiling, with optional include filter (`-PjmhQuickInclude=...`).
34+
- Publishing metadata now uses resolved dependency versions (`versionMapping`) so Gradle/Maven consumers can resolve released artifacts without missing-version failures.
3235
- JSON-RPC compliance handling for batch, notification, and invalid request edge cases.
3336
- HTTP endpoint behavior: JSON content-type enforcement and request size limit handling.
3437
- Build now uses Gradle Version Catalog (`gradle/libs.versions.toml`).

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,4 @@ Before opening a PR, make sure:
5858

5959
Do not open public issues for sensitive security vulnerabilities.
6060
Share details privately with maintainers first.
61+
See `SECURITY.md` for policy details.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ flowchart TD
126126
./gradlew apiCompat -PapiBaselineVersion=<released-version>
127127
./gradlew :jsonrpc-core:jmh
128128
./gradlew :jsonrpc-core:jmhQuick
129+
./scripts/verify-consumer-smoke.sh
129130
```
130131

131132
## Sample Application
@@ -140,5 +141,6 @@ flowchart TD
140141
## Contributing and Release
141142

142143
- Contributing guide: [`CONTRIBUTING.md`](CONTRIBUTING.md)
144+
- Security policy: [`SECURITY.md`](SECURITY.md)
143145
- Release checklist: [`docs/release-checklist.md`](docs/release-checklist.md)
144146
- Changelog: [`CHANGELOG.md`](CHANGELOG.md)

SECURITY.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Security Policy
2+
3+
## Supported Versions
4+
5+
Only the latest release line is supported with security fixes.
6+
7+
| Version | Supported |
8+
| --- | --- |
9+
| Latest stable release | Yes |
10+
| Older releases | No |
11+
| `-SNAPSHOT` builds | Best effort only |
12+
13+
## Reporting a Vulnerability
14+
15+
Do not disclose vulnerabilities through public GitHub issues.
16+
17+
Please report privately to maintainers with:
18+
19+
- affected module/version
20+
- reproduction steps or proof of concept
21+
- impact summary
22+
- suggested mitigation (if available)
23+
24+
The project will acknowledge reports, validate impact, and coordinate a fix and disclosure timeline.

build.gradle

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ subprojects {
8585
publications {
8686
mavenJava(MavenPublication) {
8787
from components.java
88+
versionMapping {
89+
usage('java-api') {
90+
fromResolutionOf('runtimeClasspath')
91+
}
92+
usage('java-runtime') {
93+
fromResolutionResult()
94+
}
95+
}
8896
pom {
8997
name = project.name
9098
description = 'JSON-RPC 2.0 library and Spring Boot starter'

docs/testing-and-quality.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ GitHub Actions runs matrix tests and compatibility checks (when release tag base
7676

7777
- `.github/workflows/ci.yml`
7878
- `.github/workflows/publish.yml`
79+
- `.github/workflows/consumer-smoke.yml` (publishes to `mavenLocal` and verifies Maven/Gradle consumer projects)
7980

8081
## Quality Expectations for Contributions
8182

scripts/verify-consumer-smoke.sh

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5+
VERSION="${JSONRPC_VERSION:-$(grep '^version=' "${ROOT_DIR}/gradle.properties" | cut -d'=' -f2)}"
6+
SPRING_BOOT_VERSION="${SPRING_BOOT_VERSION:-4.0.2}"
7+
8+
if ! command -v mvn >/dev/null 2>&1; then
9+
echo "mvn is required but was not found in PATH"
10+
exit 1
11+
fi
12+
13+
TMP_DIR="$(mktemp -d)"
14+
trap 'rm -rf "${TMP_DIR}"' EXIT
15+
16+
echo "[consumer-smoke] publish artifacts to mavenLocal (${VERSION})"
17+
"${ROOT_DIR}/gradlew" --no-daemon --configuration-cache publishToMavenLocal
18+
19+
echo "[consumer-smoke] create Maven consumer project"
20+
MAVEN_DIR="${TMP_DIR}/consumer-maven-core"
21+
mkdir -p "${MAVEN_DIR}/src/test/java/com/example"
22+
cat > "${MAVEN_DIR}/pom.xml" <<EOF
23+
<project xmlns="http://maven.apache.org/POM/4.0.0"
24+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
25+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
26+
<modelVersion>4.0.0</modelVersion>
27+
<groupId>com.example</groupId>
28+
<artifactId>consumer-maven-core</artifactId>
29+
<version>1.0.0</version>
30+
<properties>
31+
<maven.compiler.source>17</maven.compiler.source>
32+
<maven.compiler.target>17</maven.compiler.target>
33+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
34+
<junit.version>5.13.4</junit.version>
35+
</properties>
36+
<dependencies>
37+
<dependency>
38+
<groupId>io.github.limehee</groupId>
39+
<artifactId>jsonrpc-core</artifactId>
40+
<version>${VERSION}</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.junit.jupiter</groupId>
44+
<artifactId>junit-jupiter</artifactId>
45+
<version>\${junit.version}</version>
46+
<scope>test</scope>
47+
</dependency>
48+
</dependencies>
49+
<build>
50+
<plugins>
51+
<plugin>
52+
<groupId>org.apache.maven.plugins</groupId>
53+
<artifactId>maven-surefire-plugin</artifactId>
54+
<version>3.5.4</version>
55+
</plugin>
56+
</plugins>
57+
</build>
58+
</project>
59+
EOF
60+
61+
cat > "${MAVEN_DIR}/src/test/java/com/example/CoreConsumerSmokeTest.java" <<'EOF'
62+
package com.example;
63+
64+
import com.fasterxml.jackson.databind.JsonNode;
65+
import com.fasterxml.jackson.databind.ObjectMapper;
66+
import com.fasterxml.jackson.databind.node.TextNode;
67+
import com.limehee.jsonrpc.core.JsonRpcDispatchResult;
68+
import com.limehee.jsonrpc.core.JsonRpcDispatcher;
69+
import org.junit.jupiter.api.Test;
70+
71+
import static org.junit.jupiter.api.Assertions.assertEquals;
72+
73+
class CoreConsumerSmokeTest {
74+
75+
@Test
76+
void dispatchesPingFromPublishedArtifact() throws Exception {
77+
ObjectMapper mapper = new ObjectMapper();
78+
JsonRpcDispatcher dispatcher = new JsonRpcDispatcher();
79+
dispatcher.register("ping", params -> TextNode.valueOf("pong"));
80+
81+
JsonNode payload = mapper.readTree("{\"jsonrpc\":\"2.0\",\"method\":\"ping\",\"id\":1}");
82+
JsonRpcDispatchResult result = dispatcher.dispatch(payload);
83+
84+
assertEquals("pong", result.singleResponse().orElseThrow().result().asText());
85+
}
86+
}
87+
EOF
88+
89+
echo "[consumer-smoke] run Maven consumer test"
90+
mvn -f "${MAVEN_DIR}/pom.xml" -q test
91+
92+
echo "[consumer-smoke] create Gradle consumer project"
93+
GRADLE_DIR="${TMP_DIR}/consumer-gradle-starter"
94+
mkdir -p "${GRADLE_DIR}/src/main/java/com/example" "${GRADLE_DIR}/src/test/java/com/example"
95+
cat > "${GRADLE_DIR}/settings.gradle" <<'EOF'
96+
rootProject.name = 'consumer-gradle-starter'
97+
EOF
98+
99+
cat > "${GRADLE_DIR}/build.gradle" <<EOF
100+
plugins {
101+
id 'java'
102+
id 'org.springframework.boot' version '${SPRING_BOOT_VERSION}'
103+
}
104+
105+
group = 'com.example'
106+
version = '1.0.0'
107+
108+
java {
109+
toolchain {
110+
languageVersion = JavaLanguageVersion.of(17)
111+
}
112+
}
113+
114+
repositories {
115+
mavenLocal()
116+
mavenCentral()
117+
}
118+
119+
dependencies {
120+
implementation 'io.github.limehee:jsonrpc-spring-boot-starter:${VERSION}'
121+
testImplementation 'org.springframework.boot:spring-boot-starter-test:${SPRING_BOOT_VERSION}'
122+
}
123+
124+
tasks.withType(Test).configureEach {
125+
useJUnitPlatform()
126+
}
127+
EOF
128+
129+
cat > "${GRADLE_DIR}/src/main/java/com/example/ConsumerApplication.java" <<'EOF'
130+
package com.example;
131+
132+
import com.limehee.jsonrpc.core.JsonRpcMethod;
133+
import org.springframework.boot.autoconfigure.SpringBootApplication;
134+
import org.springframework.stereotype.Service;
135+
136+
@SpringBootApplication
137+
public class ConsumerApplication {
138+
}
139+
140+
@Service
141+
class GreetingRpcService {
142+
@JsonRpcMethod("greet")
143+
public String greet(GreetParams params) {
144+
return "hello " + params.name();
145+
}
146+
147+
record GreetParams(String name) {
148+
}
149+
}
150+
EOF
151+
152+
cat > "${GRADLE_DIR}/src/test/java/com/example/StarterConsumerSmokeTest.java" <<'EOF'
153+
package com.example;
154+
155+
import com.fasterxml.jackson.databind.ObjectMapper;
156+
import com.limehee.jsonrpc.core.JsonRpcDispatchResult;
157+
import com.limehee.jsonrpc.core.JsonRpcDispatcher;
158+
import org.junit.jupiter.api.Test;
159+
import org.springframework.beans.factory.annotation.Autowired;
160+
import org.springframework.boot.test.context.SpringBootTest;
161+
162+
import static org.junit.jupiter.api.Assertions.assertEquals;
163+
164+
@SpringBootTest
165+
class StarterConsumerSmokeTest {
166+
167+
@Autowired
168+
private JsonRpcDispatcher dispatcher;
169+
170+
@Test
171+
void invokesRegisteredMethodFromPublishedStarter() throws Exception {
172+
ObjectMapper mapper = new ObjectMapper();
173+
JsonRpcDispatchResult result = dispatcher.dispatch(mapper.readTree(
174+
"{\"jsonrpc\":\"2.0\",\"method\":\"greet\",\"params\":{\"name\":\"developer\"},\"id\":1}"));
175+
assertEquals("hello developer", result.singleResponse().orElseThrow().result().asText());
176+
}
177+
}
178+
EOF
179+
180+
echo "[consumer-smoke] run Gradle consumer test"
181+
"${ROOT_DIR}/gradlew" --no-daemon -p "${GRADLE_DIR}" test
182+
183+
echo "[consumer-smoke] success"

0 commit comments

Comments
 (0)