Skip to content

Commit 05ccf63

Browse files
committed
feat(forward): provide Java 8 client and packaged examples
0 parents  commit 05ccf63

446 files changed

Lines changed: 42292 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitattributes‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* text=auto
2+
mvnw text eol=lf
3+
mvnw.cmd text eol=crlf

‎.github/CODEOWNERS‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @moonyue-w

‎.github/config/main-ruleset.json‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"name": "Protect main",
3+
"target": "branch",
4+
"enforcement": "active",
5+
"conditions": {
6+
"ref_name": {
7+
"exclude": [],
8+
"include": [
9+
"~DEFAULT_BRANCH"
10+
]
11+
}
12+
},
13+
"rules": [
14+
{
15+
"type": "deletion"
16+
},
17+
{
18+
"type": "non_fast_forward"
19+
},
20+
{
21+
"type": "required_linear_history"
22+
},
23+
{
24+
"type": "pull_request",
25+
"parameters": {
26+
"required_approving_review_count": 0,
27+
"dismiss_stale_reviews_on_push": false,
28+
"require_code_owner_review": false,
29+
"require_last_push_approval": false,
30+
"required_review_thread_resolution": true,
31+
"allowed_merge_methods": [
32+
"squash"
33+
]
34+
}
35+
},
36+
{
37+
"type": "required_status_checks",
38+
"parameters": {
39+
"strict_required_status_checks_policy": true,
40+
"do_not_enforce_on_create": false,
41+
"required_status_checks": [
42+
{
43+
"context": "check (Java 8)",
44+
"integration_id": 15368
45+
},
46+
{
47+
"context": "check (Java 11)",
48+
"integration_id": 15368
49+
},
50+
{
51+
"context": "check (Java 17)",
52+
"integration_id": 15368
53+
},
54+
{
55+
"context": "check (Java 21)",
56+
"integration_id": 15368
57+
},
58+
{
59+
"context": "check (Java 25)",
60+
"integration_id": 15368
61+
},
62+
{
63+
"context": "quality",
64+
"integration_id": 15368
65+
}
66+
]
67+
}
68+
}
69+
],
70+
"bypass_actors": []
71+
}

‎.github/config/release-branch.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"name": "main", "type": "branch"}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"wait_timer": 0,
3+
"prevent_self_review": false,
4+
"reviewers": [{"type": "User", "id": 300878504}],
5+
"deployment_branch_policy": {"protected_branches": false, "custom_branch_policies": true}
6+
}

‎.github/config/tag-ruleset.json‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "protect-release-tags",
3+
"target": "tag",
4+
"enforcement": "active",
5+
"conditions": {
6+
"ref_name": {
7+
"exclude": [],
8+
"include": [
9+
"refs/tags/v*"
10+
]
11+
}
12+
},
13+
"rules": [
14+
{
15+
"type": "deletion"
16+
},
17+
{
18+
"type": "non_fast_forward"
19+
},
20+
{
21+
"type": "update"
22+
}
23+
],
24+
"bypass_actors": []
25+
}

‎.github/pull_request_template.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## Change
2+
3+
Describe the problem and resulting public behavior. For a new API, identify its contract source and the Forward coverage entry.
4+
5+
Describe any Go / Python / TypeScript behavior impact, or explain why this change is Java-specific.
6+
For incompatible changes, describe the previous behavior and the required migration.
7+
8+
## Validation
9+
10+
- [ ] SDK build, tests and Javadoc pass.
11+
- [ ] Independent examples consume the installed JAR and pass.
12+
- [ ] Java 8 package check passes; changed runtime behavior is checked on the relevant JDKs.
13+
- [ ] Documentation and changelog reflect public behavior changes.
14+
- [ ] `make quality` passes on JDK 17+ (format, versions, changelog and local links).
15+
- [ ] Any version change updates both POMs and the matching changelog entry.
16+
17+
List actual JDKs and commands, and any live scenarios run. Explain failures or untested scope; mock tests are not live verification. Keep tokens, SA Keys and signed URLs out of this PR.
18+
19+
For a QA candidate, record source SHA, artifact SHA-256 and the run/report link.
20+
State skipped or unexecuted scenarios separately from passing tests. Human QA approval is separate from automated results.

‎.github/scripts/check_package.py‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
"""Inspect the locally built SDK; no network access or publication."""
3+
4+
import argparse
5+
import hashlib
6+
import json
7+
from pathlib import Path
8+
import struct
9+
import xml.etree.ElementTree as ET
10+
import zipfile
11+
12+
13+
def require(condition, message):
14+
if not condition:
15+
raise ValueError(message)
16+
17+
18+
def check(root):
19+
pom = root / "pom.xml"
20+
xml = ET.parse(pom).getroot()
21+
ns = {"m": "http://maven.apache.org/POM/4.0.0"}
22+
artifact = xml.findtext("m:artifactId", namespaces=ns)
23+
version = xml.findtext("m:version", namespaces=ns)
24+
require(artifact == "qca-sdk" and version, "Unexpected SDK coordinates")
25+
target = root / "target"
26+
jars = [target / (artifact + "-" + version + suffix + ".jar")
27+
for suffix in ("", "-sources", "-javadoc")]
28+
for jar in jars:
29+
require(jar.is_file(), "Missing package: " + jar.name)
30+
with zipfile.ZipFile(jars[0]) as jar:
31+
names = jar.namelist()
32+
classes = [name for name in names if name.endswith(".class")]
33+
require(classes, "SDK JAR has no classes")
34+
require("com/qoder/cloudagents/ForwardClient.class" in names, "Missing Forward client")
35+
require(jar.read("META-INF/LICENSE") == (root / "LICENSE").read_bytes(), "License differs")
36+
properties = jar.read("com/qoder/cloudagents/version.properties").decode("utf-8")
37+
require("version=" + version in properties.splitlines(), "Embedded version differs from POM")
38+
for name in classes:
39+
require(name.startswith("com/qoder/cloudagents/") and "/examples/" not in name,
40+
"Bundled dependency or example class: " + name)
41+
data = jar.read(name)
42+
require(data[:4] == b"\xca\xfe\xba\xbe" and struct.unpack(">H", data[6:8])[0] == 52,
43+
"Class does not target Java 8: " + name)
44+
require(not any(name.startswith(("src/test/", "test-results/", "artifacts/"))
45+
or Path(name).name.startswith(".env") for name in names),
46+
"Unexpected local or test data in SDK JAR")
47+
with zipfile.ZipFile(jars[1]) as jar:
48+
require("com/qoder/cloudagents/ForwardClient.java" in jar.namelist(), "Missing source attachment")
49+
with zipfile.ZipFile(jars[2]) as jar:
50+
require("com/qoder/cloudagents/ForwardClient.html" in jar.namelist(), "Missing Javadoc attachment")
51+
require(not any(name.startswith("com/qoder/cloudagents/core/internal/") for name in jar.namelist()),
52+
"Internal implementation leaked into public Javadoc")
53+
files = jars + [pom]
54+
digests = {str(path.relative_to(root)): hashlib.sha256(path.read_bytes()).hexdigest() for path in files}
55+
report = {"artifact_id": artifact, "version": version, "class_count": len(classes),
56+
"class_major_version": 52, "sha256": digests}
57+
(target / "package-check.json").write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
58+
(target / "SHA256SUMS").write_text("".join(digest + " " + name + "\n" for name, digest in digests.items()), encoding="utf-8")
59+
print(json.dumps(report, indent=2))
60+
61+
62+
if __name__ == "__main__":
63+
parser = argparse.ArgumentParser(description=__doc__)
64+
parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[2])
65+
try:
66+
check(parser.parse_args().root.resolve())
67+
except (ValueError, KeyError, OSError, zipfile.BadZipFile, ET.ParseError) as error:
68+
raise SystemExit("Package check failed: " + str(error))
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env python3
2+
"""Verify Maven versions, changelog and local documentation links; never publish."""
3+
import argparse
4+
from pathlib import Path
5+
import re
6+
import sys
7+
import urllib.parse
8+
import xml.etree.ElementTree as ET
9+
10+
import release_notes
11+
12+
ROOT = Path(__file__).resolve().parents[2]
13+
NS = {"m": "http://maven.apache.org/POM/4.0.0"}
14+
15+
16+
def check_versions(root):
17+
pom = ET.parse(root / "pom.xml").getroot()
18+
version = pom.findtext("m:version", namespaces=NS)
19+
if not version or not re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9.-]+)?", version):
20+
raise ValueError("Use an explicit SDK version in pom.xml")
21+
if pom.findtext("m:groupId", namespaces=NS) != "com.qoder" or pom.findtext("m:artifactId", namespaces=NS) != "qca-sdk":
22+
raise ValueError("Unexpected Maven coordinates")
23+
consumer = ET.parse(root / "examples/pom.xml").getroot()
24+
deps = [d for d in consumer.findall("m:dependencies/m:dependency", NS)
25+
if d.findtext("m:groupId", namespaces=NS) == "com.qoder"
26+
and d.findtext("m:artifactId", namespaces=NS) == "qca-sdk"]
27+
if len(deps) != 1 or deps[0].findtext("m:version", namespaces=NS) != version:
28+
raise ValueError("examples SDK dependency must match the root POM")
29+
if consumer.findtext("m:version", namespaces=NS) != version:
30+
raise ValueError("examples project version must match the root POM")
31+
props = (root / "src/main/resources/com/qoder/cloudagents/version.properties").read_text()
32+
if "version=${project.version}" not in props.splitlines():
33+
raise ValueError("Embedded version must be filtered from project.version")
34+
entries = release_notes.parse_changelog((root / "CHANGELOG.md").read_text())
35+
if not version.endswith("-SNAPSHOT") and version not in entries:
36+
raise ValueError("Non-SNAPSHOT version requires its own changelog entry")
37+
return version
38+
39+
40+
def check_links(root):
41+
paths = [root / name for name in ("README.md", "CONTRIBUTING.md", "CHANGELOG.md",
42+
"examples/README.md", ".github/pull_request_template.md")]
43+
paths += list((root / "docs").rglob("*.md"))
44+
count = 0
45+
for path in paths:
46+
visible = "\n".join(release_notes.outside_fences(path.read_text()))
47+
for match in re.finditer(r"\[[^\]]*\]\(([^\s)]+)(?:\s+[^)]*)?\)", visible):
48+
target = match[1].strip("<>")
49+
url = urllib.parse.urlsplit(target)
50+
if url.scheme or url.netloc or not url.path:
51+
continue
52+
resolved = (path.parent / urllib.parse.unquote(url.path)).resolve()
53+
if not resolved.is_relative_to(root.resolve()) or not resolved.exists():
54+
raise ValueError(f"{path.relative_to(root)}: invalid local link {target}")
55+
count += 1
56+
return count
57+
58+
59+
def main():
60+
parser = argparse.ArgumentParser(description=__doc__)
61+
parser.add_argument("--root", type=Path, default=ROOT)
62+
args = parser.parse_args()
63+
version = check_versions(args.root)
64+
links = check_links(args.root)
65+
print(f"Repository checks passed: version {version}, {links} local links")
66+
67+
68+
if __name__ == "__main__":
69+
try:
70+
main()
71+
except (ValueError, OSError, ET.ParseError) as error:
72+
sys.exit(str(error))

‎.github/scripts/format_java.py‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python3
2+
"""Check/apply the repository's pinned Java format using JDK 17+."""
3+
import argparse
4+
import hashlib
5+
import os
6+
from pathlib import Path
7+
import subprocess
8+
import urllib.request
9+
10+
VERSION = "1.25.2"
11+
SHA256 = "25157797a0a972c2290b5bc71530c4f7ad646458025e3484412a6e5a9b8c9aa6"
12+
URL = ("https://repo.maven.apache.org/maven2/com/google/googlejavaformat/"
13+
f"google-java-format/{VERSION}/google-java-format-{VERSION}-all-deps.jar")
14+
ROOT = Path(__file__).resolve().parents[2]
15+
16+
17+
def main():
18+
parser = argparse.ArgumentParser(description=__doc__)
19+
parser.add_argument("--apply", action="store_true")
20+
args = parser.parse_args()
21+
jar = ROOT / "target/tools" / f"google-java-format-{VERSION}.jar"
22+
if not jar.exists():
23+
with urllib.request.urlopen(URL, timeout=60) as response:
24+
data = response.read()
25+
if hashlib.sha256(data).hexdigest() != SHA256:
26+
raise ValueError("Formatter download checksum mismatch")
27+
jar.parent.mkdir(parents=True, exist_ok=True)
28+
jar.write_bytes(data)
29+
if hashlib.sha256(jar.read_bytes()).hexdigest() != SHA256:
30+
raise ValueError("Cached formatter checksum mismatch")
31+
java = str(Path(os.environ["JAVA_HOME"]) / "bin/java") if os.environ.get("JAVA_HOME") else "java"
32+
files = sorted(str(p.relative_to(ROOT)) for base in (ROOT / "src", ROOT / "examples/src")
33+
for p in base.rglob("*.java"))
34+
command = [java, "-jar", str(jar)]
35+
command += ["--replace"] if args.apply else ["--dry-run", "--set-exit-if-changed"]
36+
subprocess.run(command + files, cwd=ROOT, check=True)
37+
print(f"Java format {'applied' if args.apply else 'verified'}: {len(files)} files")
38+
39+
40+
if __name__ == "__main__":
41+
try:
42+
main()
43+
except subprocess.CalledProcessError:
44+
raise SystemExit("Formatting differs; use make format on JDK 17+.")

0 commit comments

Comments
 (0)