mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-13 11:39:41 +03:00
fix(updates): reject retired next channel
This commit is contained in:
@@ -34,7 +34,7 @@ available to roughly half of IPs after three hours and all IPs after six hours.
|
||||
Eligibility uses the original publication time (`time_created`) and a SHA-256 hash
|
||||
of the channel and Cloudflare's `CF-Connecting-IP`. Each IP keeps the same rollout
|
||||
position across releases in that channel. Requests without this header wait for
|
||||
the full duration. `next` uses the `beta` channel's configuration.
|
||||
the full duration. The retired `next` channel is not available or configurable.
|
||||
|
||||
Until the active release is eligible, callers receive the newest eligible artifact
|
||||
published before it, for the same name and distribution. This also handles overlapping
|
||||
@@ -70,8 +70,8 @@ artifact. All three public API paths apply the same selection and use `Cache-Con
|
||||
no-store` because responses can depend on the User-Agent.
|
||||
|
||||
Version comparison uses semver, normalizing preview run numbers to numeric prerelease
|
||||
identifiers and historical `next` versions to `beta`. The `/api/next` channel also
|
||||
resolves to `beta`.
|
||||
identifiers and historical `next` versions to `beta`. The retired `/api/next`
|
||||
channel returns 404; use `/api/beta` instead.
|
||||
|
||||
Choose a minimum that older clients can install and that can itself consume the active
|
||||
release. For the CLI package migration, retain a package-aware release published as
|
||||
|
||||
@@ -77,7 +77,8 @@ export default {
|
||||
return new Response("Not found", { status: 404 })
|
||||
}
|
||||
|
||||
const resolved = resolveChannel(path[0])
|
||||
if (path[0] === "next") return json({ error: "Channel not found" }, 404)
|
||||
const resolved = path[0]
|
||||
const agent = request.headers.get("User-Agent")?.match(/^opencode\/(?:([^/]+)\/([^/]+)\/cli|(.*))$/)
|
||||
const current = url.searchParams.get("current") ?? agent?.[2] ?? agent?.[3]
|
||||
const source = agent?.[1] ?? current?.match(/^v?0\.0\.0-(.+)-\d+(?:\.\d+)?(?:\+.*)?$/)?.[1]
|
||||
@@ -200,6 +201,7 @@ async function configureRollout(request: Request, env: Env, prefix: string) {
|
||||
const duration = typeof input === "string" && input.trim() ? Number(input) : NaN
|
||||
if (
|
||||
!validIdentifier(channel) ||
|
||||
channel === "next" ||
|
||||
!Number.isFinite(duration) ||
|
||||
duration < 0 ||
|
||||
!Number.isFinite(duration * 3_600_000)
|
||||
@@ -209,7 +211,7 @@ async function configureRollout(request: Request, env: Env, prefix: string) {
|
||||
await env.DB.prepare(
|
||||
"INSERT INTO channel_rollout (channel, duration_hours) VALUES (?, ?) ON CONFLICT (channel) DO UPDATE SET duration_hours = excluded.duration_hours",
|
||||
)
|
||||
.bind(resolveChannel(channel), duration)
|
||||
.bind(channel, duration)
|
||||
.run()
|
||||
return Response.redirect(new URL(`${prefix}/admin`, request.url), 303)
|
||||
}
|
||||
@@ -230,7 +232,8 @@ async function admin(request: Request, env: Env, prefix: string) {
|
||||
const rollouts = await env.DB.prepare(
|
||||
`SELECT channels.channel, COALESCE(channel_rollout.duration_hours, 0) AS duration_hours
|
||||
FROM (SELECT channel FROM artifact UNION SELECT channel FROM channel_rollout) AS channels
|
||||
LEFT JOIN channel_rollout ON channel_rollout.channel = channels.channel ORDER BY channels.channel`,
|
||||
LEFT JOIN channel_rollout ON channel_rollout.channel = channels.channel
|
||||
WHERE channels.channel != 'next' ORDER BY channels.channel`,
|
||||
).all<{ channel: string; duration_hours: number }>()
|
||||
const requestedPage = Number.parseInt(url.searchParams.get("page") ?? "1", 10)
|
||||
const page = Number.isSafeInteger(requestedPage) && requestedPage > 0 ? requestedPage : 1
|
||||
@@ -453,6 +456,7 @@ function parseArtifact(input: Record<string, unknown>): ArtifactInput | Response
|
||||
function parseKey(input: Record<string, unknown>): Omit<ArtifactInput, "metadata"> | Response {
|
||||
if (
|
||||
!validIdentifier(input.channel) ||
|
||||
input.channel === "next" ||
|
||||
!validIdentifier(input.name) ||
|
||||
!validIdentifier(input.distribution) ||
|
||||
!validVersion(input.version)
|
||||
|
||||
Reference in New Issue
Block a user