• 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 · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc