• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FlysonBot / Mastermind / 23101718182

15 Mar 2026 02:36AM UTC coverage: 86.088% (-0.1%) from 86.207%
23101718182

push

github

FlysonBot
chore: add a user notice for openjdk-21 pointer tag bug

0 of 1 new or added line in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

625 of 726 relevant lines covered (86.09%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

48.51
/src/main/python/mastermind/java_setup.py
1
"""
2
Ensures the JRE and JAR are available before the program starts.
3
Returns (jre_path, jar_path) — jre_path is None on Android (system JDK used directly).
4

5
On Android (Termux): requires openjdk-21 via `pkg install openjdk-21`.
6
On other platforms:  uses the bundled JRE zip if available, else downloads a JDK
7
                     and builds a trimmed JRE into target/mastermind-jre.
8
"""
9

10
import platform
1 ✔
11
import shutil
1 ✔
12
import subprocess
1 ✔
13
import sys
1 ✔
14
import time
1 ✔
15
import zipfile
1 ✔
16
from pathlib import Path
1 ✔
17

18
from mastermind.ui import console
1 ✔
19

20
# --- Paths ---
21

22
_PKG = Path(__file__).parent
1 ✔
23
_ROOT = _PKG.parents[3]  # repo root, only valid in dev (not in installed package)
1 ✔
24
_SRC_JAVA = _ROOT / "src" / "main" / "java"
1 ✔
25
_CLASSES = _ROOT / "target" / "classes"
1 ✔
26
_JDK = _ROOT / "target" / "java-jdk"
1 ✔
27
_BUNDLED_JAR = _PKG / "mastermind-solver.jar"
1 ✔
28
_BUNDLED_JRE = _PKG / "jre"
1 ✔
29
_CACHED_JRE = _PKG / "mastermind-jre"
1 ✔
30

31
# --- Platform config ---
32

33
_PLATFORM_MAP = {
1 ✔
34
    ("linux", "x86_64"): "linux-x64",
35
    ("linux", "amd64"): "linux-x64",
36
    ("windows", "amd64"): "windows-x64",
37
    ("windows", "x86_64"): "windows-x64",
38
    ("darwin", "x86_64"): "mac-x64",
39
    ("darwin", "amd64"): "mac-x64",
40
    ("darwin", "arm64"): "mac-aarch64",
41
    ("darwin", "aarch64"): "mac-aarch64",
42
}
43

44

45
# --- Public API ---
46

47

48
def ensure_ready():
1 ✔
49
    """Return (jre_path, jar_path), setting up whatever is missing."""
50
    if platform.system().lower() == "android":
1 ✔
51
        return _ensure_android()
×
52
    return _ensure_desktop()
1 ✔
53

54

55
# --- Platform-specific setup ---
56

57

58
def _ensure_android():
1 ✔
59
    """On Android/Termux, verify the system JDK is present and return (None, jar)."""
60
    if not shutil.which("java"):
×
61
        console.print("[red]Java is not installed.[/red] Please run:")
×
62
        console.print("    [cyan]pkg install openjdk-21[/cyan]")
×
63
        sys.exit(1)
×
64

65
    if not _BUNDLED_JAR.exists():
×
66
        console.print(
×
67
            "[red]Bundled JAR not found.[/red] Please re-clone or re-download the repository."
68
        )
69
        sys.exit(1)
×
70

NEW
71
    console.print(
×
72
        "[yellow]Note:[/yellow] You're currently running this application on Android. "
73
        "openjdk-21 has a known bug on this platform that may cause occasional pointer tag "
74
        "crashes. This is not a bug in this application and cannot be fixed. If it happens, "
75
        "simply restart the app."
76
    )
77

UNCOV
78
    return None, _BUNDLED_JAR
×
79

80

81
def _ensure_desktop():
1 ✔
82
    """Return (jre, jar), extracting or building the JRE/JAR as needed."""
83
    jre_path = _resolve_jre()
1 ✔
84
    jar_path = _BUNDLED_JAR if _BUNDLED_JAR.exists() else None
1 ✔
85

86
    if jre_path and jar_path:
1 ✔
87
        return jre_path, jar_path
1 ✔
88

89
    # Fallback: download a JDK to build whatever is missing
90
    _download_jdk()
×
91
    try:
×
92
        if not jre_path:
×
93
            _build_jre()
×
94
            jre_path = _CACHED_JRE
×
95

96
        if not jar_path:
×
97
            _build_jar()
×
98
            jar_path = _BUNDLED_JAR
×
99
    finally:
100
        shutil.rmtree(_JDK, ignore_errors=True)
×
101

102
    return jre_path, jar_path
×
103

104

105
def _resolve_jre():
1 ✔
106
    """Return the JRE path if ready, extracting the bundled zip if needed."""
107
    if _CACHED_JRE.exists():
1 ✔
108
        return _CACHED_JRE
×
109

110
    os_name = platform.system().lower()
1 ✔
111
    arch = platform.machine().lower()
1 ✔
112
    name = _PLATFORM_MAP.get((os_name, arch))
1 ✔
113

114
    if name:
1 ✔
115
        zip_path = _BUNDLED_JRE / f"{name}.zip"
1 ✔
116
        if zip_path.exists():
1 ✔
117
            _extract_jre(zip_path)
1 ✔
118
            return _CACHED_JRE
1 ✔
119

120
    return None
×
121

122

123
# --- JRE extraction ---
124

125

126
def _extract_jre(zip_path: Path):
1 ✔
127
    t = time.time()
1 ✔
128
    with console.status(f"Extracting JRE from [cyan]{zip_path.name}[/cyan]..."):
1 ✔
129
        _CACHED_JRE.mkdir(parents=True, exist_ok=True)
1 ✔
130

131
        with zipfile.ZipFile(zip_path, "r") as zf:
1 ✔
132
            zf.extractall(_CACHED_JRE)
1 ✔
133

134
        # Restore execute permissions on binaries (lost during zip on Unix)
135
        for f in (_CACHED_JRE / "bin").iterdir():
1 ✔
136
            f.chmod(f.stat().st_mode | 0o111)
1 ✔
137

138
    console.print(f"[green]✓[/green] JRE extracted. ({time.time() - t:.1f}s)")
1 ✔
139

140

141
# --- Fallback: build from downloaded JDK ---
142

143

144
def _download_jdk():
1 ✔
145
    import jdk  # install-jdk library
×
146

147
    t = time.time()
×
148
    with console.status("Downloading JDK..."):
×
149
        jdk.install(version="21", path=str(_JDK))
×
150
    size_mb = (
×
151
        sum(f.stat().st_size for f in _JDK.rglob("*") if f.is_file()) / 1024 / 1024
152
    )
153
    console.print(
×
154
        f"[green]✓[/green] JDK ready. ({time.time() - t:.1f}s, {size_mb:.0f} MB)"
155
    )
156

157

158
def _build_jre():
1 ✔
159
    jlink = next(_JDK.rglob("jlink"), None)
×
160
    if not jlink:
×
161
        raise RuntimeError("jlink not found in downloaded JDK")
×
162

163
    t = time.time()
×
164
    with console.status("Building trimmed JRE..."):
×
165
        subprocess.run(
×
166
            [
167
                str(jlink),
168
                "--add-modules",
169
                "java.base",
170
                "--output",
171
                str(_CACHED_JRE),
172
                "--strip-debug",
173
                "--no-header-files",
174
                "--no-man-pages",
175
            ],
176
            check=True,
177
        )
178
    console.print(f"[green]✓[/green] JRE ready. ({time.time() - t:.1f}s)")
×
179

180

181
def _build_jar():
1 ✔
182
    javac = next(_JDK.rglob("javac"), None)
×
183
    jar_tool = next(_JDK.rglob("jar"), None)
×
184
    if not javac:
×
185
        raise RuntimeError("javac not found in downloaded JDK")
×
186
    if not jar_tool:
×
187
        raise RuntimeError("jar not found in downloaded JDK")
×
188

189
    sources = [str(p) for p in _SRC_JAVA.rglob("*.java")]
×
190
    _CLASSES.mkdir(parents=True, exist_ok=True)
×
191
    _BUNDLED_JAR.parent.mkdir(parents=True, exist_ok=True)
×
192

193
    t = time.time()
×
194
    with console.status("Compiling Java sources..."):
×
195
        subprocess.run([str(javac), "-d", str(_CLASSES)] + sources, check=True)
×
196
        subprocess.run(
×
197
            [
198
                str(jar_tool),
199
                "--create",
200
                "--file",
201
                str(_BUNDLED_JAR),
202
                "-C",
203
                str(_CLASSES),
204
                ".",
205
            ],
206
            check=True,
207
        )
208
    console.print(f"[green]✓[/green] JAR ready. ({time.time() - t:.1f}s)")
×
209

210

211
if __name__ == "__main__":
1 ✔
212
    jre, jar = ensure_ready()
×
213
    console.print(f"JRE: {jre}")
×
214
    console.print(f"JAR: {jar}")
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc