Let manual update checks bypass rollout
Automatic desktop update checks still respect rollout admission. Manual checks carry an explicit intent through the app and desktop updater service, and the up-to-date state now shows when the last check completed.
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { UUID } from "builder-util-runtime";
|
||||
import { z } from "zod";
|
||||
|
||||
export type AppReleaseChannel = "stable" | "beta";
|
||||
export type AppUpdateCheckIntent = "automatic" | "manual";
|
||||
|
||||
export const rolloutManifestSchema = z.object({
|
||||
rolloutHours: z
|
||||
.union([z.number(), z.string().transform(Number)])
|
||||
.pipe(z.number().finite().nonnegative())
|
||||
.optional()
|
||||
.catch(undefined),
|
||||
releaseDate: z.string().optional().catch(undefined),
|
||||
});
|
||||
|
||||
export function shouldAdmitAppUpdate(args: {
|
||||
channel: AppReleaseChannel;
|
||||
intent: AppUpdateCheckIntent;
|
||||
rolloutHours: number | undefined;
|
||||
releaseDate: string | undefined;
|
||||
now: number;
|
||||
bucket: number;
|
||||
}): boolean {
|
||||
if (args.intent === "manual") return true;
|
||||
if (args.channel !== "stable") return true;
|
||||
if (args.rolloutHours == null) return true;
|
||||
if (args.rolloutHours === 0) return true;
|
||||
if (!args.releaseDate) return true;
|
||||
|
||||
const releaseTime = new Date(args.releaseDate).getTime();
|
||||
if (Number.isNaN(releaseTime)) return true;
|
||||
|
||||
const ageHours = (args.now - releaseTime) / 3_600_000;
|
||||
if (ageHours < 0) return false;
|
||||
|
||||
const pct = Math.min(100, (ageHours / args.rolloutHours) * 100);
|
||||
return args.bucket * 100 < pct;
|
||||
}
|
||||
|
||||
export function bucketFromStagingUserId(stagingUserId: string): number {
|
||||
return UUID.parse(stagingUserId).readUInt32BE(12) / 0x100000000;
|
||||
}
|
||||
Reference in New Issue
Block a user