Skip to content

Commit

Permalink
fix: only show update message once per day (#1742)
Browse files Browse the repository at this point in the history
  • Loading branch information
marvinhagemeister authored Sep 6, 2023
1 parent 20d5630 commit 0acd1a5
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 15 deletions.
10 changes: 10 additions & 0 deletions src/dev/update_check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { colors, join, semver } from "./deps.ts";

export interface CheckFile {
last_checked: string;
last_shown?: string;
latest_version: string;
current_version: string;
}
Expand Down Expand Up @@ -115,8 +116,12 @@ export async function updateCheck(
const currentVersion = semver.parse(checkFile.current_version);
const latestVersion = semver.parse(checkFile.latest_version);
if (
(!checkFile.last_shown ||
Date.now() >= new Date(checkFile.last_shown).getTime() + interval) &&
semver.lt(currentVersion, latestVersion)
) {
checkFile.last_shown = new Date().toISOString();

const current = colors.bold(colors.rgb8(checkFile.current_version, 208));
const latest = colors.bold(colors.rgb8(checkFile.latest_version, 121));
console.log(
Expand All @@ -130,6 +135,11 @@ export async function updateCheck(
console.log();
}

// Migrate old format to current
if (!checkFile.last_shown) {
checkFile.last_shown = new Date().toISOString();
}

const raw = JSON.stringify(checkFile, null, 2);
await Deno.writeTextFile(filePath, raw);
}
178 changes: 164 additions & 14 deletions tests/cli_update_check_test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import { colors, join } from "../src/server/deps.ts";
import {
assert,
assertEquals,
assertMatch,
assertNotEquals,
assertNotMatch,
} from "$std/testing/asserts.ts";
import versions from "../versions.json" assert { type: "json" };
import { CheckFile } from "$fresh/src/dev/update_check.ts";
import { WEEK } from "$fresh/src/dev/deps.ts";

function getStdOutput(
out: Deno.CommandOutput,
): { stdout: string; stderr: string } {
const decoder = new TextDecoder();
const stdout = colors.stripColor(decoder.decode(out.stdout));

const decoderErr = new TextDecoder();
const stderr = colors.stripColor(decoderErr.decode(out.stderr));

return { stdout, stderr };
}

Deno.test({
name: "stores update check file in $HOME/fresh",
async fn() {
Expand All @@ -27,6 +41,7 @@ Deno.test({
current_version: versions[0],
latest_version: "99.99.999",
last_checked: text.last_checked,
last_shown: text.last_shown,
});

await Deno.remove(tmpDirName, { recursive: true });
Expand All @@ -48,10 +63,11 @@ Deno.test({
TEST_HOME: tmpDirName,
LATEST_VERSION: "1.30.0",
},
stderr: "piped",
stdout: "piped",
}).output();

const decoder = new TextDecoder();
const stdout = colors.stripColor(decoder.decode(out.stdout));
const { stdout } = getStdOutput(out);
assertNotMatch(stdout, /Fresh 1\.30\.0 is available/);

await Deno.remove(tmpDirName, { recursive: true });
Expand Down Expand Up @@ -82,11 +98,11 @@ Deno.test({
TEST_HOME: tmpDirName,
LATEST_VERSION: "999.999.0",
},
stderr: "piped",
stdout: "piped",
}).output();

const decoder = new TextDecoder();

const stdout = colors.stripColor(decoder.decode(out.stdout));
const { stdout } = getStdOutput(out);
assertMatch(stdout, /Fresh 999\.999\.0 is available/);

// Updates check file
Expand All @@ -95,6 +111,7 @@ Deno.test({
current_version: versions[0],
latest_version: "999.999.0",
last_checked: text.last_checked,
last_shown: text.last_shown,
});

await Deno.remove(tmpDirName, { recursive: true });
Expand All @@ -106,7 +123,6 @@ Deno.test({
name: "only fetch new version defined by interval",
async fn(t) {
const tmpDirName = await Deno.makeTempDir();
const decoder = new TextDecoder();

await t.step("fetches latest version initially", async () => {
const out = await new Deno.Command(Deno.execPath(), {
Expand All @@ -117,9 +133,11 @@ Deno.test({
TEST_HOME: tmpDirName,
LATEST_VERSION: "1.30.0",
},
stderr: "piped",
stdout: "piped",
}).output();

const stdout = colors.stripColor(decoder.decode(out.stdout));
const { stdout } = getStdOutput(out);
assertMatch(stdout, /fetching latest version/);
});

Expand All @@ -132,9 +150,11 @@ Deno.test({
TEST_HOME: tmpDirName,
LATEST_VERSION: "1.30.0",
},
stderr: "piped",
stdout: "piped",
}).output();

const stdout = colors.stripColor(decoder.decode(out.stdout));
const { stdout } = getStdOutput(out);
assertNotMatch(stdout, /fetching latest version/);
});

Expand All @@ -149,7 +169,7 @@ Deno.test({
},
}).output();

const stdout = colors.stripColor(decoder.decode(out.stdout));
const { stdout } = getStdOutput(out);
assertMatch(stdout, /fetching latest version/);
});

Expand All @@ -176,13 +196,15 @@ Deno.test({
const out = await new Deno.Command(Deno.execPath(), {
args: ["run", "-A", "./tests/fixture_update_check/mod.ts"],
env: {
CI: "false",
TEST_HOME: tmpDirName,
LATEST_VERSION: versions[0],
},
stderr: "piped",
stdout: "piped",
}).output();

const decoder = new TextDecoder();
const stdout = colors.stripColor(decoder.decode(out.stdout));
const { stdout } = getStdOutput(out);
assertNotMatch(stdout, /Fresh .* is available/);

await Deno.remove(tmpDirName, { recursive: true });
Expand All @@ -208,16 +230,144 @@ Deno.test({
const out = await new Deno.Command(Deno.execPath(), {
args: ["run", "-A", "./tests/fixture_update_check/mod.ts"],
env: {
CI: "false",
TEST_HOME: tmpDirName,
LATEST_VERSION: versions[0],
CURRENT_VERSION: "99999.9999.00",
CURRENT_VERSION: "99999.9999.0",
},
stderr: "piped",
stdout: "piped",
}).output();

const decoder = new TextDecoder();
const stdout = colors.stripColor(decoder.decode(out.stdout));
const { stdout } = getStdOutput(out);
assertNotMatch(stdout, /Fresh .* is available/);

await Deno.remove(tmpDirName, { recursive: true });
},
});

Deno.test("migrates to last_shown property", async () => {
const tmpDirName = await Deno.makeTempDir();

const checkFile: CheckFile = {
latest_version: "1.4.0",
current_version: "1.2.0",
last_checked: new Date().toISOString(),
};

await Deno.writeTextFile(
join(tmpDirName, "latest.json"),
JSON.stringify(checkFile, null, 2),
);

const out = await new Deno.Command(Deno.execPath(), {
args: ["run", "-A", "./tests/fixture_update_check/mod.ts"],
env: {
CI: "false",
TEST_HOME: tmpDirName,
CURRENT_VERSION: "1.2.0",
LATEST_VERSION: "99999.9999.0",
},
stderr: "piped",
stdout: "piped",
}).output();

const { stdout } = getStdOutput(out);
assertMatch(stdout, /Fresh .* is available/);

const checkFileAfter = JSON.parse(
await Deno.readTextFile(
join(tmpDirName, "latest.json"),
),
);

assert(
typeof checkFileAfter.last_shown === "string",
"Did not write last_shown " + JSON.stringify(checkFileAfter, null, 2),
);

await Deno.remove(tmpDirName, { recursive: true });
});

Deno.test("doesn't show update if last_shown + interval >= today", async () => {
const tmpDirName = await Deno.makeTempDir();

const todayMinus1Hour = new Date();
todayMinus1Hour.setHours(todayMinus1Hour.getHours() - 1);

const checkFile: CheckFile = {
current_version: "1.2.0",
latest_version: "1.6.0",
last_checked: new Date().toISOString(),
last_shown: todayMinus1Hour.toISOString(),
};

await Deno.writeTextFile(
join(tmpDirName, "latest.json"),
JSON.stringify(checkFile, null, 2),
);

const out = await new Deno.Command(Deno.execPath(), {
args: ["run", "-A", "./tests/fixture_update_check/mod.ts"],
env: {
CI: "false",
TEST_HOME: tmpDirName,
CURRENT_VERSION: "1.2.0",
LATEST_VERSION: "99999.9999.0",
},
stderr: "piped",
stdout: "piped",
}).output();

const { stdout } = getStdOutput(out);
assertNotMatch(stdout, /Fresh .* is available/);

await Deno.remove(tmpDirName, { recursive: true });
});

Deno.test(
"shows update if last_shown + interval < today",
async () => {
const tmpDirName = await Deno.makeTempDir();

const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);

const checkFile: CheckFile = {
current_version: "1.2.0",
latest_version: "1.8.0",
last_checked: new Date().toISOString(),
last_shown: yesterday.toISOString(),
};

await Deno.writeTextFile(
join(tmpDirName, "latest.json"),
JSON.stringify(checkFile, null, 2),
);

const out = await new Deno.Command(Deno.execPath(), {
args: ["run", "-A", "./tests/fixture_update_check/mod.ts"],
env: {
CI: "false",
TEST_HOME: tmpDirName,
CURRENT_VERSION: versions[0],
LATEST_VERSION: "99999.9999.0",
},
stderr: "piped",
stdout: "piped",
}).output();

const { stdout } = getStdOutput(out);
assertMatch(stdout, /Fresh .* is available/);

const checkFileAfter = JSON.parse(
await Deno.readTextFile(
join(tmpDirName, "latest.json"),
),
);

assertNotEquals(checkFileAfter.last_shown, yesterday.toISOString());

await Deno.remove(tmpDirName, { recursive: true });
},
);
3 changes: 2 additions & 1 deletion tests/fixture_update_check/mod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { updateCheck } from "$fresh/src/dev/update_check.ts";
import { DAY } from "$fresh/src/dev/deps.ts";

// deno-lint-ignore require-await
async function getLatestVersion() {
Expand All @@ -11,7 +12,7 @@ async function getCurrentVersion() {
return Deno.env.get("CURRENT_VERSION")!;
}

const interval = +(Deno.env.get("UPDATE_INTERVAL") ?? 1000);
const interval = +(Deno.env.get("UPDATE_INTERVAL") ?? DAY);
await updateCheck(
interval,
() => Deno.env.get("TEST_HOME")!,
Expand Down

0 comments on commit 0acd1a5

Please sign in to comment.