Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions scripts/sync-to-d1.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import { fetchWithRetry } from "./lib/fetch-with-retry.js";
const DOCS_DIR = join(process.cwd(), "docs");
const MANUAL_OVERRIDES_FILE = join(DOCS_DIR, "manual-overrides.json");

// Prerelease version regex - ported from mise
// Fallback for tools that do not expose explicit prerelease metadata in TOML.
// Prefer `prerelease = true` from mise where present; this catches older and
// non-metadata sources so latest_stable_version does not regress.
const PRERELEASE_REGEX =
/(-src|-dev|-latest|-stm|[-.](rc|pre)|-milestone|-alpha|-beta|-next|([abc])\d+$|snapshot|master)/i;

function isPrerelease(version) {
return PRERELEASE_REGEX.test(version);
function isPrerelease(version, data) {
return data?.prerelease === true || PRERELEASE_REGEX.test(version);
}

function toISOString(value) {
Expand Down Expand Up @@ -190,8 +192,8 @@ function processTomlFile(filePath) {

let latestStableVersion = null;
for (let i = versions.length - 1; i >= 0; i--) {
const [version] = versions[i];
if (!isPrerelease(version)) {
const [version, data] = versions[i];
if (!isPrerelease(version, data)) {
latestStableVersion = version;
break;
}
Expand Down
6 changes: 4 additions & 2 deletions scripts/update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ generate_toml_file() {
local error_output
error_output=$(mktemp)

# Try to get JSON with timestamps from mise ls-remote --json.
# Try to get JSON with timestamps/release URLs/prerelease flags from
# mise ls-remote --prerelease --json. The TOML path can carry prerelease
# metadata, so collect the superset and let clients filter by that flag.
# Pass the rotated per-tool token via GITHUB_API_TOKEN so this call
# isn't rate-limited by the workflow's single shared MISE_GITHUB_TOKEN.
# Without this, ~58% of tools per run hit GitHub's 5000/hr authenticated
Expand All @@ -350,7 +352,7 @@ generate_toml_file() {
# plain-text path — losing `release_url` and `created_at` for any new
# version that wasn't already in the existing TOML.
local json_output
if json_output=$(GITHUB_API_TOKEN="$token" mise ls-remote --json "$tool" 2>/dev/null) && [ -n "$json_output" ]; then
if json_output=$(GITHUB_API_TOKEN="$token" mise ls-remote --prerelease --json "$tool" 2>/dev/null) && [ -n "$json_output" ]; then
local json_count
json_count=$(printf '%s' "$json_output" | jq 'if type == "array" then length else 0 end' 2>/dev/null || echo 0)
if [ "${json_count:-0}" -gt 0 ]; then
Expand Down
5 changes: 3 additions & 2 deletions web/src/components/VersionsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface Version {
version: string;
created_at?: string | null;
release_url?: string | null;
prerelease?: boolean;
}

type VersionSortKey = "default" | "downloads" | "released";
Expand Down Expand Up @@ -357,7 +358,7 @@ export function VersionsTable({

// Filter by prerelease
if (hidePrerelease) {
result = result.filter((v) => !isPrerelease(v.version));
result = result.filter((v) => !isPrerelease(v));
}

// Filter by version prefix
Expand Down Expand Up @@ -414,7 +415,7 @@ export function VersionsTable({
(v) => getDistribution(v.version, tool) === distribution,
);
}
return result.filter((v) => isPrerelease(v.version)).length;
return result.filter((v) => isPrerelease(v)).length;
}, [versions, distribution, tool]);

// Build timeline from versions filtered by distribution and version prefix only
Expand Down
49 changes: 9 additions & 40 deletions web/src/lib/versions.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,20 @@
/**
* Version filtering utilities - ported from mise
* @see https://github.com/jdx/mise/blob/main/src/plugins/mod.rs
*/

/**
* Regex to identify prerelease/development/unstable versions.
* Versions matching this pattern should be excluded when showing stable versions.
*
* Matches:
* - -src, -dev, -latest, -stm (build markers)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported function isPrereleaseVersion is never imported externally

Low Severity

The newly introduced isPrereleaseVersion is exported but never imported anywhere outside web/src/lib/versions.ts. It's only called internally by isPrerelease in the same file. The export keyword can be removed to keep the module's public surface minimal.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 97bc9c6. Configure here.

* - -rc, .rc (release candidates)
* - -milestone (milestone releases)
* - -alpha, -beta (pre-releases)
* - -pre, .pre (pre-releases)
* - -next (next version markers)
* - a1, b2, c3 etc. (single letter + digits)
* - snapshot, SNAPSHOT (snapshot builds)
* - master (development branch)
* Regex fallback for backends that do not expose explicit prerelease metadata.
* Prefer stored `prerelease = true` where available; this only covers older
* and non-metadata sources such as Java/Core plugin version lists.
*/
const PRERELEASE_REGEX =
/(-src|-dev|-latest|-stm|[-.](rc|pre)|-milestone|-alpha|-beta|-next|([abc])\d+$|snapshot|master)/i;

/**
* Check if a version string appears to be a prerelease/development version
*/
export function isPrerelease(version: string): boolean {
export function isPrereleaseVersion(version: string): boolean {
return PRERELEASE_REGEX.test(version);
}

/**
* Filter an array of versions to only include stable releases
*/
export function filterStableVersions<T extends { version: string }>(
versions: T[],
): T[] {
return versions.filter((v) => !isPrerelease(v.version));
}

/**
* Get the latest stable version from an array of versions
* Assumes versions are ordered oldest to newest
*/
export function getLatestStableVersion<T extends { version: string }>(
versions: T[],
): T | undefined {
const stable = filterStableVersions(versions);
return stable[stable.length - 1];
export function isPrerelease(version: {
version: string;
prerelease?: boolean | null;
}): boolean {
return version.prerelease === true || isPrereleaseVersion(version.version);
}

/**
Expand Down
6 changes: 5 additions & 1 deletion web/src/pages/[...tool].toml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface VersionRow {
version: string;
created_at: string | null;
release_url: string | null;
prerelease: number;
}

// Legacy endpoint: GET /:tool.toml - serves TOML version file from D1
Expand Down Expand Up @@ -55,7 +56,7 @@ export const GET: APIRoute = async ({ request, params, locals }) => {
// Get versions ordered by sort_order (semantic version order from TOML file)
// Only include versions from mise ls-remote (not user-tracked installs)
const versions = await db.all<VersionRow>(sql`
SELECT version, created_at, release_url
SELECT version, created_at, release_url, prerelease
FROM versions
WHERE tool_id = ${toolId} AND from_mise = 1
ORDER BY sort_order ASC, id ASC
Expand Down Expand Up @@ -100,6 +101,9 @@ export const GET: APIRoute = async ({ request, params, locals }) => {
if (v.release_url) {
parts.push(`release_url = "${v.release_url}"`);
}
if (v.prerelease === 1) {
parts.push("prerelease = true");
}

if (parts.length > 0) {
lines.push(`"${v.version}" = { ${parts.join(", ")} }`);
Expand Down
6 changes: 3 additions & 3 deletions web/src/pages/[...tool].ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ export const GET: APIRoute = async ({ params, locals }) => {

const toolId = (toolResult[0] as { id: number }).id;

// Get versions ordered by sort_order (semantic version order from TOML file)
// Only include versions from mise ls-remote (not user-tracked installs)
// Get versions ordered by sort_order (semantic version order from TOML file).
// Plain text cannot carry prerelease metadata, so keep it stable-only.
const versions = await db.all<{ version: string }>(sql`
SELECT version FROM versions WHERE tool_id = ${toolId} AND from_mise = 1 ORDER BY sort_order ASC, id ASC
SELECT version FROM versions WHERE tool_id = ${toolId} AND from_mise = 1 AND prerelease = 0 ORDER BY sort_order ASC, id ASC
Comment thread
greptile-apps[bot] marked this conversation as resolved.
`);

if (versions.length === 0) {
Expand Down
6 changes: 3 additions & 3 deletions web/src/pages/tools/[...tool].ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ export const GET: APIRoute = async ({ params, locals }) => {

const toolId = (toolResult[0] as { id: number }).id;

// Get versions ordered by sort_order (semantic version order from TOML file)
// Only include versions from mise ls-remote (not user-tracked installs)
// Get versions ordered by sort_order (semantic version order from TOML file).
// Plain text cannot carry prerelease metadata, so keep it stable-only.
const versions = await db.all<{ version: string }>(sql`
SELECT version
FROM versions
WHERE tool_id = ${toolId} AND from_mise = 1
WHERE tool_id = ${toolId} AND from_mise = 1 AND prerelease = 0
ORDER BY sort_order ASC, id ASC
`);

Expand Down
7 changes: 4 additions & 3 deletions web/src/pages/tools/[tool].astro
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ if (!toolMeta) {
}

// Load versions from D1 database
let versions: Array<{ version: string; created_at?: string | null; release_url?: string | null }> = [];
let versions: Array<{ version: string; created_at?: string | null; release_url?: string | null; prerelease?: boolean }> = [];
try {
// Get tool_id first
const toolResult = await db.all<{ id: number }>(sql`
Expand All @@ -39,8 +39,8 @@ try {

if (toolResult.length > 0) {
const toolId = toolResult[0].id;
const versionRows = await db.all<{ version: string; created_at: string | null; release_url: string | null }>(sql`
SELECT version, created_at, release_url
const versionRows = await db.all<{ version: string; created_at: string | null; release_url: string | null; prerelease: number }>(sql`
SELECT version, created_at, release_url, prerelease
FROM versions
WHERE tool_id = ${toolId}
ORDER BY sort_order ASC, id ASC
Expand All @@ -49,6 +49,7 @@ try {
version: row.version,
created_at: row.created_at || null,
release_url: row.release_url || null,
prerelease: row.prerelease === 1,
}));
}
} catch (e) {
Expand Down
6 changes: 5 additions & 1 deletion web/src/pages/tools/[tool].toml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface VersionRow {
version: string;
created_at: string | null;
release_url: string | null;
prerelease: number;
}

export const GET: APIRoute = async ({ request, params, locals }) => {
Expand Down Expand Up @@ -53,7 +54,7 @@ export const GET: APIRoute = async ({ request, params, locals }) => {
// Get versions ordered by sort_order (semantic version order from TOML file)
// Only include versions from mise ls-remote (not user-tracked installs)
const versions = await db.all<VersionRow>(sql`
SELECT version, created_at, release_url
SELECT version, created_at, release_url, prerelease
FROM versions
WHERE tool_id = ${toolId} AND from_mise = 1
ORDER BY sort_order ASC, id ASC
Expand Down Expand Up @@ -98,6 +99,9 @@ export const GET: APIRoute = async ({ request, params, locals }) => {
if (v.release_url) {
parts.push(`release_url = "${v.release_url}"`);
}
if (v.prerelease === 1) {
parts.push("prerelease = true");
}

if (parts.length > 0) {
lines.push(`"${v.version}" = { ${parts.join(", ")} }`);
Expand Down