mirror of
https://github.com/CloakHQ/CloakBrowser.git
synced 2026-06-23 11:41:46 +02:00
feat: per-platform Chromium versioning and build number support
- Add PLATFORM_CHROMIUM_VERSIONS map (Linux=v145, macOS=v142) - Add get_chromium_version()/getChromiumVersion() for platform-specific version - Make auto-update check release assets before offering updates - Scope version markers per-platform (latest_version_linux-x64) - Support 5th version segment for hotfix builds (e.g. 145.0.7632.109.2) - Derive AVAILABLE_PLATFORMS from version map
This commit is contained in:
+29
-13
@@ -10,10 +10,20 @@ from pathlib import Path
|
||||
from ._version import __version__
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Chromium version shipped with this release
|
||||
# Chromium version shipped with this release.
|
||||
# Different platforms may ship different versions (e.g. Linux gets v145 first,
|
||||
# macOS stays on v142 until Mac builds are ready).
|
||||
# CHROMIUM_VERSION is the latest across all platforms (for display/reference).
|
||||
# Use get_chromium_version() for the current platform's actual version.
|
||||
# ---------------------------------------------------------------------------
|
||||
CHROMIUM_VERSION = "145.0.7632.109"
|
||||
|
||||
PLATFORM_CHROMIUM_VERSIONS: dict[str, str] = {
|
||||
"linux-x64": "145.0.7632.109",
|
||||
"darwin-arm64": "142.0.7444.175",
|
||||
"darwin-x64": "142.0.7444.175",
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Default stealth arguments passed to the patched Chromium binary.
|
||||
# These activate source-level fingerprint patches compiled into the binary.
|
||||
@@ -70,9 +80,14 @@ SUPPORTED_PLATFORMS: dict[tuple[str, str], str] = {
|
||||
("Darwin", "x86_64"): "darwin-x64",
|
||||
}
|
||||
|
||||
# Platforms with pre-built binaries available for download.
|
||||
# Update this set as new platform builds are released.
|
||||
AVAILABLE_PLATFORMS: set[str] = {"linux-x64", "darwin-arm64", "darwin-x64"}
|
||||
# Platforms with pre-built binaries available for download (derived from version map).
|
||||
AVAILABLE_PLATFORMS: set[str] = set(PLATFORM_CHROMIUM_VERSIONS.keys())
|
||||
|
||||
|
||||
def get_chromium_version() -> str:
|
||||
"""Return the Chromium version for the current platform."""
|
||||
tag = get_platform_tag()
|
||||
return PLATFORM_CHROMIUM_VERSIONS.get(tag, CHROMIUM_VERSION)
|
||||
|
||||
|
||||
def get_platform_tag() -> str:
|
||||
@@ -105,7 +120,7 @@ def get_cache_dir() -> Path:
|
||||
|
||||
def get_binary_dir(version: str | None = None) -> Path:
|
||||
"""Return the directory for a Chromium version binary."""
|
||||
v = version or CHROMIUM_VERSION
|
||||
v = version or get_chromium_version()
|
||||
return get_cache_dir() / f"chromium-{v}"
|
||||
|
||||
|
||||
@@ -141,23 +156,24 @@ def check_platform_available() -> None:
|
||||
|
||||
|
||||
def get_effective_version() -> str:
|
||||
"""Return the best available version: auto-updated if available, else hardcoded.
|
||||
"""Return the best available version: auto-updated if available, else platform default.
|
||||
|
||||
Reads the latest_version marker file from the cache directory.
|
||||
Returns CHROMIUM_VERSION if no update has been downloaded.
|
||||
Reads a platform-scoped marker file from the cache directory.
|
||||
Returns the platform's hardcoded version if no update has been downloaded.
|
||||
"""
|
||||
marker = get_cache_dir() / "latest_version"
|
||||
base = get_chromium_version()
|
||||
marker = get_cache_dir() / f"latest_version_{get_platform_tag()}"
|
||||
if marker.exists():
|
||||
try:
|
||||
version = marker.read_text().strip()
|
||||
if version and _version_newer(version, CHROMIUM_VERSION):
|
||||
if version and _version_newer(version, base):
|
||||
# Verify the binary actually exists
|
||||
binary = get_binary_path(version)
|
||||
if binary.exists():
|
||||
return version
|
||||
except (ValueError, OSError):
|
||||
pass
|
||||
return CHROMIUM_VERSION
|
||||
return base
|
||||
|
||||
|
||||
def _version_tuple(v: str) -> tuple[int, ...]:
|
||||
@@ -187,14 +203,14 @@ GITHUB_DOWNLOAD_BASE_URL = (
|
||||
|
||||
def get_download_url(version: str | None = None) -> str:
|
||||
"""Return the full download URL for the current platform's binary archive."""
|
||||
v = version or CHROMIUM_VERSION
|
||||
v = version or get_chromium_version()
|
||||
tag = get_platform_tag()
|
||||
return f"{DOWNLOAD_BASE_URL}/chromium-v{v}/cloakbrowser-{tag}.tar.gz"
|
||||
|
||||
|
||||
def get_fallback_download_url(version: str | None = None) -> str:
|
||||
"""Return the GitHub Releases fallback URL for the binary archive."""
|
||||
v = version or CHROMIUM_VERSION
|
||||
v = version or get_chromium_version()
|
||||
tag = get_platform_tag()
|
||||
return f"{GITHUB_DOWNLOAD_BASE_URL}/chromium-v{v}/cloakbrowser-{tag}.tar.gz"
|
||||
|
||||
|
||||
+21
-11
@@ -30,6 +30,7 @@ from .config import (
|
||||
get_binary_dir,
|
||||
get_binary_path,
|
||||
get_cache_dir,
|
||||
get_chromium_version,
|
||||
get_download_url,
|
||||
get_effective_version,
|
||||
get_fallback_download_url,
|
||||
@@ -76,18 +77,19 @@ def ensure_binary() -> str:
|
||||
_maybe_trigger_update_check()
|
||||
return str(binary_path)
|
||||
|
||||
# Fall back to hardcoded version if effective version binary doesn't exist
|
||||
if effective != CHROMIUM_VERSION:
|
||||
# Fall back to platform's hardcoded version if effective version binary doesn't exist
|
||||
platform_version = get_chromium_version()
|
||||
if effective != platform_version:
|
||||
fallback_path = get_binary_path()
|
||||
if fallback_path.exists() and _is_executable(fallback_path):
|
||||
logger.debug("Binary found in cache: %s", fallback_path)
|
||||
_maybe_trigger_update_check()
|
||||
return str(fallback_path)
|
||||
|
||||
# Download hardcoded version
|
||||
# Download platform's hardcoded version
|
||||
logger.info(
|
||||
"Stealth Chromium %s not found. Downloading for %s...",
|
||||
CHROMIUM_VERSION,
|
||||
platform_version,
|
||||
get_platform_tag(),
|
||||
)
|
||||
_download_and_extract()
|
||||
@@ -168,7 +170,7 @@ def _verify_download_checksum(file_path: Path, version: str | None = None) -> No
|
||||
|
||||
def _fetch_checksums(version: str | None = None) -> dict[str, str] | None:
|
||||
"""Fetch SHA256SUMS file for a version. Returns {filename: hash} or None."""
|
||||
v = version or CHROMIUM_VERSION
|
||||
v = version or get_chromium_version()
|
||||
has_custom_url = os.environ.get("CLOAKBROWSER_DOWNLOAD_URL")
|
||||
|
||||
# Build URL list — respect custom URL contract (no GitHub fallback)
|
||||
@@ -384,7 +386,7 @@ def check_for_update() -> str | None:
|
||||
latest = _get_latest_chromium_version()
|
||||
if latest is None:
|
||||
return None
|
||||
if not _version_newer(latest, CHROMIUM_VERSION):
|
||||
if not _version_newer(latest, get_chromium_version()):
|
||||
return None
|
||||
|
||||
binary_dir = get_binary_dir(latest)
|
||||
@@ -420,15 +422,22 @@ def _should_check_for_update() -> bool:
|
||||
|
||||
|
||||
def _get_latest_chromium_version() -> str | None:
|
||||
"""Hit GitHub Releases API, return latest chromium-v* version string or None."""
|
||||
"""Hit GitHub Releases API, return latest chromium-v* version for this platform.
|
||||
|
||||
Checks that the release has a binary asset for the current platform,
|
||||
so Linux-only releases won't be offered to macOS users.
|
||||
"""
|
||||
try:
|
||||
resp = httpx.get(
|
||||
GITHUB_API_URL, params={"per_page": 10}, timeout=10.0
|
||||
)
|
||||
resp.raise_for_status()
|
||||
platform_tarball = f"cloakbrowser-{get_platform_tag()}.tar.gz"
|
||||
for release in resp.json():
|
||||
tag = release.get("tag_name", "")
|
||||
if tag.startswith("chromium-v") and not release.get("draft"):
|
||||
asset_names = {a["name"] for a in release.get("assets", [])}
|
||||
if platform_tarball in asset_names:
|
||||
return tag.removeprefix("chromium-v")
|
||||
return None
|
||||
except Exception:
|
||||
@@ -437,10 +446,10 @@ def _get_latest_chromium_version() -> str | None:
|
||||
|
||||
|
||||
def _write_version_marker(version: str) -> None:
|
||||
"""Write the latest version marker to cache dir."""
|
||||
"""Write the latest version marker for this platform to cache dir."""
|
||||
cache_dir = get_cache_dir()
|
||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
marker = cache_dir / "latest_version"
|
||||
marker = cache_dir / f"latest_version_{get_platform_tag()}"
|
||||
# Write to temp file then rename for atomicity
|
||||
tmp = marker.with_suffix(".tmp")
|
||||
tmp.write_text(version)
|
||||
@@ -455,10 +464,11 @@ def _check_and_download_update() -> None:
|
||||
check_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
check_file.write_text(str(time.time()))
|
||||
|
||||
platform_version = get_chromium_version()
|
||||
latest = _get_latest_chromium_version()
|
||||
if latest is None:
|
||||
return
|
||||
if not _version_newer(latest, CHROMIUM_VERSION):
|
||||
if not _version_newer(latest, platform_version):
|
||||
return
|
||||
|
||||
# Already downloaded?
|
||||
@@ -469,7 +479,7 @@ def _check_and_download_update() -> None:
|
||||
logger.info(
|
||||
"Newer Chromium available: %s (current: %s). Downloading in background...",
|
||||
latest,
|
||||
CHROMIUM_VERSION,
|
||||
platform_version,
|
||||
)
|
||||
_download_and_extract(version=latest)
|
||||
_write_version_marker(latest)
|
||||
|
||||
+25
-10
@@ -8,10 +8,20 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Chromium version shipped with this release
|
||||
// Chromium version shipped with this release.
|
||||
// Different platforms may ship different versions (e.g. Linux gets v145 first,
|
||||
// macOS stays on v142 until Mac builds are ready).
|
||||
// CHROMIUM_VERSION is the latest across all platforms (for display/reference).
|
||||
// Use getChromiumVersion() for the current platform's actual version.
|
||||
// ---------------------------------------------------------------------------
|
||||
export const CHROMIUM_VERSION = "145.0.7632.109";
|
||||
|
||||
export const PLATFORM_CHROMIUM_VERSIONS: Record<string, string> = {
|
||||
"linux-x64": "145.0.7632.109",
|
||||
"darwin-arm64": "142.0.7444.175",
|
||||
"darwin-x64": "142.0.7444.175",
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Platform detection
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -22,9 +32,13 @@ const SUPPORTED_PLATFORMS: Record<string, string> = {
|
||||
"darwin-x64": "darwin-x64",
|
||||
};
|
||||
|
||||
// Platforms with pre-built binaries available for download.
|
||||
// Update this set as new platform builds are released.
|
||||
const AVAILABLE_PLATFORMS = new Set(["linux-x64", "darwin-arm64", "darwin-x64"]);
|
||||
// Platforms with pre-built binaries available for download (derived from version map).
|
||||
const AVAILABLE_PLATFORMS = new Set(Object.keys(PLATFORM_CHROMIUM_VERSIONS));
|
||||
|
||||
export function getChromiumVersion(): string {
|
||||
const tag = getPlatformTag();
|
||||
return PLATFORM_CHROMIUM_VERSIONS[tag] ?? CHROMIUM_VERSION;
|
||||
}
|
||||
|
||||
export function getPlatformTag(): string {
|
||||
const platform = process.platform;
|
||||
@@ -56,7 +70,7 @@ export function getCacheDir(): string {
|
||||
}
|
||||
|
||||
export function getBinaryDir(version?: string): string {
|
||||
return path.join(getCacheDir(), `chromium-${version || CHROMIUM_VERSION}`);
|
||||
return path.join(getCacheDir(), `chromium-${version || getChromiumVersion()}`);
|
||||
}
|
||||
|
||||
export function getBinaryPath(version?: string): string {
|
||||
@@ -95,23 +109,24 @@ export const GITHUB_DOWNLOAD_BASE_URL =
|
||||
"https://github.com/CloakHQ/cloakbrowser/releases/download";
|
||||
|
||||
export function getDownloadUrl(version?: string): string {
|
||||
const v = version || CHROMIUM_VERSION;
|
||||
const v = version || getChromiumVersion();
|
||||
const tag = getPlatformTag();
|
||||
return `${DOWNLOAD_BASE_URL}/chromium-v${v}/cloakbrowser-${tag}.tar.gz`;
|
||||
}
|
||||
|
||||
export function getFallbackDownloadUrl(version?: string): string {
|
||||
const v = version || CHROMIUM_VERSION;
|
||||
const v = version || getChromiumVersion();
|
||||
const tag = getPlatformTag();
|
||||
return `${GITHUB_DOWNLOAD_BASE_URL}/chromium-v${v}/cloakbrowser-${tag}.tar.gz`;
|
||||
}
|
||||
|
||||
export function getEffectiveVersion(): string {
|
||||
const marker = path.join(getCacheDir(), "latest_version");
|
||||
const base = getChromiumVersion();
|
||||
const marker = path.join(getCacheDir(), `latest_version_${getPlatformTag()}`);
|
||||
try {
|
||||
if (fs.existsSync(marker)) {
|
||||
const version = fs.readFileSync(marker, "utf-8").trim();
|
||||
if (version && versionNewer(version, CHROMIUM_VERSION)) {
|
||||
if (version && versionNewer(version, base)) {
|
||||
const binary = getBinaryPath(version);
|
||||
if (fs.existsSync(binary)) {
|
||||
return version;
|
||||
@@ -121,7 +136,7 @@ export function getEffectiveVersion(): string {
|
||||
} catch {
|
||||
// Marker unreadable — fall back to hardcoded
|
||||
}
|
||||
return CHROMIUM_VERSION;
|
||||
return base;
|
||||
}
|
||||
|
||||
export function parseVersion(v: string): number[] {
|
||||
|
||||
+22
-12
@@ -14,7 +14,6 @@ import { extract as tarExtract } from "tar";
|
||||
|
||||
import type { BinaryInfo } from "./types.js";
|
||||
import {
|
||||
CHROMIUM_VERSION,
|
||||
DOWNLOAD_BASE_URL,
|
||||
GITHUB_API_URL,
|
||||
GITHUB_DOWNLOAD_BASE_URL,
|
||||
@@ -22,6 +21,7 @@ import {
|
||||
getBinaryDir,
|
||||
getBinaryPath,
|
||||
getCacheDir,
|
||||
getChromiumVersion,
|
||||
getDownloadUrl,
|
||||
getEffectiveVersion,
|
||||
getFallbackDownloadUrl,
|
||||
@@ -66,8 +66,9 @@ export async function ensureBinary(): Promise<string> {
|
||||
return binaryPath;
|
||||
}
|
||||
|
||||
// Fall back to hardcoded version if effective version binary doesn't exist
|
||||
if (effective !== CHROMIUM_VERSION) {
|
||||
// Fall back to platform's hardcoded version if effective version binary doesn't exist
|
||||
const platformVersion = getChromiumVersion();
|
||||
if (effective !== platformVersion) {
|
||||
const fallbackPath = getBinaryPath();
|
||||
if (fs.existsSync(fallbackPath) && isExecutable(fallbackPath)) {
|
||||
maybeTriggerUpdateCheck();
|
||||
@@ -75,9 +76,9 @@ export async function ensureBinary(): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
// Download hardcoded version
|
||||
// Download platform's hardcoded version
|
||||
console.log(
|
||||
`[cloakbrowser] Stealth Chromium ${CHROMIUM_VERSION} not found. Downloading for ${getPlatformTag()}...`
|
||||
`[cloakbrowser] Stealth Chromium ${platformVersion} not found. Downloading for ${getPlatformTag()}...`
|
||||
);
|
||||
await downloadAndExtract();
|
||||
|
||||
@@ -120,7 +121,7 @@ export function binaryInfo(): BinaryInfo {
|
||||
/** Manually check for a newer Chromium version. Returns new version or null. */
|
||||
export async function checkForUpdate(): Promise<string | null> {
|
||||
const latest = await getLatestChromiumVersion();
|
||||
if (!latest || !versionNewer(latest, CHROMIUM_VERSION)) return null;
|
||||
if (!latest || !versionNewer(latest, getChromiumVersion())) return null;
|
||||
|
||||
const binaryDir = getBinaryDir(latest);
|
||||
if (fs.existsSync(binaryDir)) {
|
||||
@@ -209,7 +210,7 @@ async function verifyDownloadChecksum(filePath: string, version?: string): Promi
|
||||
}
|
||||
|
||||
async function fetchChecksums(version?: string): Promise<Map<string, string> | null> {
|
||||
const v = version || CHROMIUM_VERSION;
|
||||
const v = version || getChromiumVersion();
|
||||
const hasCustomUrl = !!process.env.CLOAKBROWSER_DOWNLOAD_URL;
|
||||
|
||||
// Respect custom URL contract — no GitHub fallback when custom URL is set
|
||||
@@ -437,7 +438,8 @@ function shouldCheckForUpdate(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getLatestChromiumVersion(): Promise<string | null> {
|
||||
/** @internal Exported for testing only. */
|
||||
export async function getLatestChromiumVersion(): Promise<string | null> {
|
||||
try {
|
||||
const resp = await fetch(`${GITHUB_API_URL}?per_page=10`, {
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
@@ -446,10 +448,17 @@ async function getLatestChromiumVersion(): Promise<string | null> {
|
||||
const releases = (await resp.json()) as Array<{
|
||||
tag_name: string;
|
||||
draft: boolean;
|
||||
assets: Array<{ name: string }>;
|
||||
}>;
|
||||
const platformTarball = `cloakbrowser-${getPlatformTag()}.tar.gz`;
|
||||
for (const release of releases) {
|
||||
if (release.tag_name.startsWith("chromium-v") && !release.draft) {
|
||||
return release.tag_name.replace("chromium-v", "");
|
||||
const assetNames = new Set(
|
||||
(release.assets ?? []).map((a) => a.name)
|
||||
);
|
||||
if (assetNames.has(platformTarball)) {
|
||||
return release.tag_name.replace(/^chromium-v/, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -461,7 +470,7 @@ async function getLatestChromiumVersion(): Promise<string | null> {
|
||||
function writeVersionMarker(version: string): void {
|
||||
const cacheDir = getCacheDir();
|
||||
fs.mkdirSync(cacheDir, { recursive: true });
|
||||
const marker = path.join(cacheDir, "latest_version");
|
||||
const marker = path.join(cacheDir, `latest_version_${getPlatformTag()}`);
|
||||
const tmp = `${marker}.tmp`;
|
||||
fs.writeFileSync(tmp, version);
|
||||
fs.renameSync(tmp, marker);
|
||||
@@ -477,8 +486,9 @@ async function checkAndDownloadUpdate(): Promise<void> {
|
||||
String(Date.now())
|
||||
);
|
||||
|
||||
const platformVersion = getChromiumVersion();
|
||||
const latest = await getLatestChromiumVersion();
|
||||
if (!latest || !versionNewer(latest, CHROMIUM_VERSION)) return;
|
||||
if (!latest || !versionNewer(latest, platformVersion)) return;
|
||||
|
||||
// Already downloaded?
|
||||
if (fs.existsSync(getBinaryDir(latest))) {
|
||||
@@ -487,7 +497,7 @@ async function checkAndDownloadUpdate(): Promise<void> {
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[cloakbrowser] Newer Chromium available: ${latest} (current: ${CHROMIUM_VERSION}). Downloading in background...`
|
||||
`[cloakbrowser] Newer Chromium available: ${latest} (current: ${platformVersion}). Downloading in background...`
|
||||
);
|
||||
await downloadAndExtract(latest);
|
||||
writeVersionMarker(latest);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
CHROMIUM_VERSION,
|
||||
getChromiumVersion,
|
||||
getDefaultStealthArgs,
|
||||
getCacheDir,
|
||||
getBinaryDir,
|
||||
@@ -10,7 +11,7 @@ import { _buildArgsForTest } from "../src/playwright.js";
|
||||
|
||||
describe("config", () => {
|
||||
it("CHROMIUM_VERSION matches expected format", () => {
|
||||
expect(CHROMIUM_VERSION).toMatch(/^\d+\.\d+\.\d+\.\d+$/);
|
||||
expect(CHROMIUM_VERSION).toMatch(/^\d+\.\d+\.\d+\.\d+(\.\d+)?$/);
|
||||
});
|
||||
|
||||
it("getDefaultStealthArgs returns expected flags", () => {
|
||||
@@ -53,14 +54,14 @@ describe("config", () => {
|
||||
expect(dir).toContain(".cloakbrowser");
|
||||
});
|
||||
|
||||
it("getBinaryDir includes version", () => {
|
||||
it("getBinaryDir includes platform version", () => {
|
||||
const dir = getBinaryDir();
|
||||
expect(dir).toContain(`chromium-${CHROMIUM_VERSION}`);
|
||||
expect(dir).toContain(`chromium-${getChromiumVersion()}`);
|
||||
});
|
||||
|
||||
it("getDownloadUrl contains version and platform tag", () => {
|
||||
it("getDownloadUrl contains platform version and platform tag", () => {
|
||||
const url = getDownloadUrl();
|
||||
expect(url).toContain(CHROMIUM_VERSION);
|
||||
expect(url).toContain(getChromiumVersion());
|
||||
expect(url).toContain("cloakbrowser-");
|
||||
expect(url).toContain(".tar.gz");
|
||||
expect(url).toContain("cloakbrowser.dev");
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { binaryInfo } from "../src/download.js";
|
||||
import { CHROMIUM_VERSION } from "../src/config.js";
|
||||
import { getChromiumVersion } from "../src/config.js";
|
||||
|
||||
describe("binaryInfo", () => {
|
||||
it("returns correct structure", () => {
|
||||
const info = binaryInfo();
|
||||
|
||||
expect(info.version).toBe(CHROMIUM_VERSION);
|
||||
expect(info.version).toBe(getChromiumVersion());
|
||||
expect(info.platform).toMatch(/^(linux|darwin)-(x64|arm64)$/);
|
||||
expect(info.binaryPath).toBeTruthy();
|
||||
expect(typeof info.installed).toBe("boolean");
|
||||
|
||||
+100
-4
@@ -1,11 +1,14 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import {
|
||||
CHROMIUM_VERSION,
|
||||
getChromiumVersion,
|
||||
getDownloadUrl,
|
||||
getEffectiveVersion,
|
||||
getPlatformTag,
|
||||
parseVersion,
|
||||
versionNewer,
|
||||
} from "../src/config.js";
|
||||
import { getLatestChromiumVersion } from "../src/download.js";
|
||||
|
||||
describe("version comparison", () => {
|
||||
it("parseVersion handles 4-part versions", () => {
|
||||
@@ -32,13 +35,29 @@ describe("version comparison", () => {
|
||||
it("major bump wins over minor", () => {
|
||||
expect(versionNewer("143.0.0.0", "142.9.9999.999")).toBe(true);
|
||||
});
|
||||
|
||||
it("parseVersion handles 5-part build numbers", () => {
|
||||
expect(parseVersion("145.0.7632.109.2")).toEqual([145, 0, 7632, 109, 2]);
|
||||
});
|
||||
|
||||
it("build bump detected", () => {
|
||||
expect(versionNewer("145.0.7632.109.3", "145.0.7632.109.2")).toBe(true);
|
||||
});
|
||||
|
||||
it("build suffix newer than no suffix", () => {
|
||||
expect(versionNewer("145.0.7632.109.2", "145.0.7632.109")).toBe(true);
|
||||
});
|
||||
|
||||
it("no suffix older than build suffix", () => {
|
||||
expect(versionNewer("145.0.7632.109", "145.0.7632.109.2")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("download URL", () => {
|
||||
it("uses chromium-v prefix and cloakbrowser repo", () => {
|
||||
const url = getDownloadUrl();
|
||||
expect(url).toContain("cloakbrowser.dev");
|
||||
expect(url).toContain(`chromium-v${CHROMIUM_VERSION}`);
|
||||
expect(url).toContain(`chromium-v${getChromiumVersion()}`);
|
||||
expect(url.endsWith(".tar.gz")).toBe(true);
|
||||
});
|
||||
|
||||
@@ -53,9 +72,86 @@ describe("download URL", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("latest version (platform-aware)", () => {
|
||||
const platformTarball = `cloakbrowser-${getPlatformTag()}.tar.gz`;
|
||||
|
||||
function makeAssets(platforms: string[]) {
|
||||
return platforms.map((p) => ({ name: `cloakbrowser-${p}.tar.gz` }));
|
||||
}
|
||||
|
||||
function mockFetch(releases: Array<Record<string, unknown>>) {
|
||||
return vi.spyOn(globalThis, "fetch").mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => releases,
|
||||
} as Response);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("returns version when release has platform asset", async () => {
|
||||
mockFetch([
|
||||
{
|
||||
tag_name: "chromium-v145.0.7718.0",
|
||||
draft: false,
|
||||
assets: makeAssets(["linux-x64", "darwin-arm64", "darwin-x64"]),
|
||||
},
|
||||
]);
|
||||
expect(await getLatestChromiumVersion()).toBe("145.0.7718.0");
|
||||
});
|
||||
|
||||
it("skips release without platform asset", async () => {
|
||||
const spy = mockFetch([
|
||||
{
|
||||
tag_name: "chromium-v145.0.7718.0",
|
||||
draft: false,
|
||||
assets: makeAssets(["linux-x64"]), // Linux only
|
||||
},
|
||||
{
|
||||
tag_name: "chromium-v142.0.7444.175",
|
||||
draft: false,
|
||||
assets: makeAssets(["linux-x64", "darwin-arm64", "darwin-x64"]),
|
||||
},
|
||||
]);
|
||||
const result = await getLatestChromiumVersion();
|
||||
const tag = getPlatformTag();
|
||||
if (tag === "linux-x64") {
|
||||
expect(result).toBe("145.0.7718.0");
|
||||
} else {
|
||||
expect(result).toBe("142.0.7444.175");
|
||||
}
|
||||
});
|
||||
|
||||
it("returns null when no release has platform asset", async () => {
|
||||
mockFetch([
|
||||
{
|
||||
tag_name: "chromium-v145.0.7718.0",
|
||||
draft: false,
|
||||
assets: [{ name: "cloakbrowser-windows-x64.tar.gz" }],
|
||||
},
|
||||
]);
|
||||
expect(await getLatestChromiumVersion()).toBeNull();
|
||||
});
|
||||
|
||||
it("skips draft releases", async () => {
|
||||
const all = ["linux-x64", "darwin-arm64", "darwin-x64"];
|
||||
mockFetch([
|
||||
{ tag_name: "chromium-v999.0.0.0", draft: true, assets: makeAssets(all) },
|
||||
{ tag_name: "chromium-v145.0.7718.0", draft: false, assets: makeAssets(all) },
|
||||
]);
|
||||
expect(await getLatestChromiumVersion()).toBe("145.0.7718.0");
|
||||
});
|
||||
|
||||
it("returns null on network error", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("timeout"));
|
||||
expect(await getLatestChromiumVersion()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("effective version", () => {
|
||||
it("returns CHROMIUM_VERSION when no marker exists", () => {
|
||||
it("returns platform version when no marker exists", () => {
|
||||
// Default behavior — no marker file in test environment
|
||||
expect(getEffectiveVersion()).toBe(CHROMIUM_VERSION);
|
||||
expect(getEffectiveVersion()).toBe(getChromiumVersion());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""Basic launch tests for cloakbrowser."""
|
||||
|
||||
import pytest
|
||||
from cloakbrowser import launch, launch_async, binary_info, CHROMIUM_VERSION
|
||||
from cloakbrowser import launch, launch_async, binary_info
|
||||
from cloakbrowser.config import get_chromium_version
|
||||
|
||||
|
||||
def test_binary_info():
|
||||
@@ -11,7 +12,7 @@ def test_binary_info():
|
||||
assert "platform" in info
|
||||
assert "binary_path" in info
|
||||
assert "installed" in info
|
||||
assert info["version"] == CHROMIUM_VERSION
|
||||
assert info["version"] == get_chromium_version()
|
||||
|
||||
|
||||
def test_launch_and_close():
|
||||
|
||||
+87
-15
@@ -12,8 +12,10 @@ from cloakbrowser.config import (
|
||||
CHROMIUM_VERSION,
|
||||
_version_newer,
|
||||
_version_tuple,
|
||||
get_chromium_version,
|
||||
get_download_url,
|
||||
get_effective_version,
|
||||
get_platform_tag,
|
||||
)
|
||||
from cloakbrowser.download import (
|
||||
_get_latest_chromium_version,
|
||||
@@ -41,12 +43,27 @@ class TestVersionComparison:
|
||||
def test_major_bump(self):
|
||||
assert _version_newer("143.0.0.0", "142.9.9999.999") is True
|
||||
|
||||
def test_5th_segment_parsing(self):
|
||||
assert _version_tuple("145.0.7632.109.2") == (145, 0, 7632, 109, 2)
|
||||
|
||||
def test_build_bump(self):
|
||||
assert _version_newer("145.0.7632.109.3", "145.0.7632.109.2") is True
|
||||
|
||||
def test_build_suffix_newer_than_no_suffix(self):
|
||||
assert _version_newer("145.0.7632.109.2", "145.0.7632.109") is True
|
||||
|
||||
def test_no_suffix_older_than_build_suffix(self):
|
||||
assert _version_newer("145.0.7632.109", "145.0.7632.109.2") is False
|
||||
|
||||
def test_new_chromium_beats_old_build(self):
|
||||
assert _version_newer("146.0.0.0", "145.0.7632.109.2") is True
|
||||
|
||||
|
||||
class TestDownloadUrl:
|
||||
def test_default_url_format(self):
|
||||
url = get_download_url()
|
||||
assert "cloakbrowser.dev" in url
|
||||
assert f"chromium-v{CHROMIUM_VERSION}" in url
|
||||
assert f"chromium-v{get_chromium_version()}" in url
|
||||
assert url.endswith(".tar.gz")
|
||||
|
||||
def test_custom_version_url(self):
|
||||
@@ -111,30 +128,42 @@ class TestShouldCheckForUpdate:
|
||||
|
||||
|
||||
class TestEffectiveVersion:
|
||||
def test_no_marker_returns_hardcoded(self, tmp_path):
|
||||
def test_no_marker_returns_platform_version(self, tmp_path):
|
||||
with patch.dict(os.environ, {"CLOAKBROWSER_CACHE_DIR": str(tmp_path)}):
|
||||
assert get_effective_version() == CHROMIUM_VERSION
|
||||
assert get_effective_version() == get_chromium_version()
|
||||
|
||||
def test_marker_with_newer_version(self, tmp_path):
|
||||
with patch.dict(os.environ, {"CLOAKBROWSER_CACHE_DIR": str(tmp_path)}):
|
||||
marker = tmp_path / "latest_version"
|
||||
marker = tmp_path / f"latest_version_{get_platform_tag()}"
|
||||
marker.write_text("999.0.0.0")
|
||||
# Binary doesn't exist, so should fall back
|
||||
assert get_effective_version() == CHROMIUM_VERSION
|
||||
assert get_effective_version() == get_chromium_version()
|
||||
|
||||
def test_marker_with_older_version_ignored(self, tmp_path):
|
||||
with patch.dict(os.environ, {"CLOAKBROWSER_CACHE_DIR": str(tmp_path)}):
|
||||
marker = tmp_path / "latest_version"
|
||||
marker = tmp_path / f"latest_version_{get_platform_tag()}"
|
||||
marker.write_text("100.0.0.0")
|
||||
assert get_effective_version() == CHROMIUM_VERSION
|
||||
assert get_effective_version() == get_chromium_version()
|
||||
|
||||
|
||||
class TestGetLatestVersion:
|
||||
def test_parses_chromium_tag(self):
|
||||
"""Tests for _get_latest_chromium_version with platform-aware asset checking."""
|
||||
|
||||
def _make_assets(self, platforms: list[str]) -> list[dict]:
|
||||
"""Helper to build asset list from platform tags."""
|
||||
return [{"name": f"cloakbrowser-{p}.tar.gz"} for p in platforms]
|
||||
|
||||
def _platform_tarball(self) -> str:
|
||||
return f"cloakbrowser-{get_platform_tag()}.tar.gz"
|
||||
|
||||
def test_parses_chromium_tag_with_platform_asset(self):
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = [
|
||||
{"tag_name": "chromium-v145.0.7718.0", "draft": False},
|
||||
{"tag_name": "chromium-v142.0.7444.175", "draft": False},
|
||||
{
|
||||
"tag_name": "chromium-v145.0.7718.0",
|
||||
"draft": False,
|
||||
"assets": self._make_assets(["linux-x64", "darwin-arm64", "darwin-x64"]),
|
||||
},
|
||||
]
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
@@ -142,11 +171,37 @@ class TestGetLatestVersion:
|
||||
result = _get_latest_chromium_version()
|
||||
assert result == "145.0.7718.0"
|
||||
|
||||
def test_skips_draft_releases(self):
|
||||
def test_skips_release_without_platform_asset(self):
|
||||
"""If latest release has no asset for our platform, fall back to older release."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = [
|
||||
{"tag_name": "chromium-v999.0.0.0", "draft": True},
|
||||
{"tag_name": "chromium-v145.0.7718.0", "draft": False},
|
||||
{
|
||||
"tag_name": "chromium-v145.0.7718.0",
|
||||
"draft": False,
|
||||
"assets": self._make_assets(["linux-x64"]), # Linux only
|
||||
},
|
||||
{
|
||||
"tag_name": "chromium-v142.0.7444.175",
|
||||
"draft": False,
|
||||
"assets": self._make_assets(["linux-x64", "darwin-arm64", "darwin-x64"]),
|
||||
},
|
||||
]
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
with patch("cloakbrowser.download.httpx.get", return_value=mock_response):
|
||||
result = _get_latest_chromium_version()
|
||||
tag = get_platform_tag()
|
||||
if tag == "linux-x64":
|
||||
assert result == "145.0.7718.0"
|
||||
else:
|
||||
assert result == "142.0.7444.175"
|
||||
|
||||
def test_skips_draft_releases(self):
|
||||
mock_response = MagicMock()
|
||||
all_platforms = ["linux-x64", "darwin-arm64", "darwin-x64"]
|
||||
mock_response.json.return_value = [
|
||||
{"tag_name": "chromium-v999.0.0.0", "draft": True, "assets": self._make_assets(all_platforms)},
|
||||
{"tag_name": "chromium-v145.0.7718.0", "draft": False, "assets": self._make_assets(all_platforms)},
|
||||
]
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
@@ -156,9 +211,10 @@ class TestGetLatestVersion:
|
||||
|
||||
def test_skips_non_chromium_tags(self):
|
||||
mock_response = MagicMock()
|
||||
all_platforms = ["linux-x64", "darwin-arm64", "darwin-x64"]
|
||||
mock_response.json.return_value = [
|
||||
{"tag_name": "v0.2.0", "draft": False},
|
||||
{"tag_name": "chromium-v145.0.7718.0", "draft": False},
|
||||
{"tag_name": "v0.2.0", "draft": False, "assets": self._make_assets(all_platforms)},
|
||||
{"tag_name": "chromium-v145.0.7718.0", "draft": False, "assets": self._make_assets(all_platforms)},
|
||||
]
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
@@ -166,6 +222,22 @@ class TestGetLatestVersion:
|
||||
result = _get_latest_chromium_version()
|
||||
assert result == "145.0.7718.0"
|
||||
|
||||
def test_returns_none_when_no_platform_assets(self):
|
||||
"""If no release has our platform, return None."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = [
|
||||
{
|
||||
"tag_name": "chromium-v145.0.7718.0",
|
||||
"draft": False,
|
||||
"assets": [{"name": "cloakbrowser-windows-x64.tar.gz"}],
|
||||
},
|
||||
]
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
with patch("cloakbrowser.download.httpx.get", return_value=mock_response):
|
||||
result = _get_latest_chromium_version()
|
||||
assert result is None
|
||||
|
||||
def test_network_error_returns_none(self):
|
||||
with patch("cloakbrowser.download.httpx.get", side_effect=Exception("timeout")):
|
||||
result = _get_latest_chromium_version()
|
||||
|
||||
Reference in New Issue
Block a user