diff --git a/src/behaviours/update-app/__snapshots__/checking-for-updates-automatically.test.ts.snap b/src/behaviours/update-app/__snapshots__/checking-for-updates-automatically.test.ts.snap
new file mode 100644
index 0000000000..b8012d142a
--- /dev/null
+++ b/src/behaviours/update-app/__snapshots__/checking-for-updates-automatically.test.ts.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`checking for updates automatically given updater is enabled and configuration exists, when started renders 1`] = `
+
+
+
+`;
diff --git a/src/behaviours/update-app/checking-for-updates-automatically.test.ts b/src/behaviours/update-app/checking-for-updates-automatically.test.ts
new file mode 100644
index 0000000000..d28926b428
--- /dev/null
+++ b/src/behaviours/update-app/checking-for-updates-automatically.test.ts
@@ -0,0 +1,117 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
+import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
+import type { RenderResult } from "@testing-library/react";
+import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
+import publishIsConfiguredInjectable from "../../main/update-app/publish-is-configured.injectable";
+import type { AsyncFnMock } from "@async-fn/jest";
+import asyncFn from "@async-fn/jest";
+import checkForUpdatesInjectable from "../../main/update-app/check-for-updates/check-for-updates.injectable";
+import startCheckingForUpdatesInjectable
+ from "../../main/update-app/periodical-check-for-updates/start-checking-for-updates.injectable";
+
+const ENOUGH_TIME = 1000 * 60 * 60 * 2;
+
+describe("checking for updates automatically", () => {
+ let applicationBuilder: ApplicationBuilder;
+ let checkForUpdatesMock: AsyncFnMock<() => Promise>;
+
+ beforeEach(() => {
+ jest.useFakeTimers();
+
+ applicationBuilder = getApplicationBuilder();
+
+ applicationBuilder.beforeApplicationStart(({ mainDi }) => {
+ mainDi.unoverride(startCheckingForUpdatesInjectable);
+
+ checkForUpdatesMock = asyncFn();
+
+ mainDi.override(
+ checkForUpdatesInjectable,
+ () => checkForUpdatesMock,
+ );
+ });
+ });
+
+ describe("given updater is enabled and configuration exists, when started", () => {
+ let rendered: RenderResult;
+
+ beforeEach(async () => {
+ applicationBuilder.beforeApplicationStart(({ mainDi }) => {
+ mainDi.override(electronUpdaterIsActiveInjectable, () => true);
+ mainDi.override(publishIsConfiguredInjectable, () => true);
+ });
+
+ rendered = await applicationBuilder.render();
+ });
+
+ it("renders", () => {
+ expect(rendered.baseElement).toMatchSnapshot();
+ });
+
+ it("checks for updates", () => {
+ expect(checkForUpdatesMock).toHaveBeenCalled();
+ });
+
+ it("when just not enough time passes, does not check for updates again automatically yet", () => {
+ checkForUpdatesMock.mockClear();
+
+ jest.advanceTimersByTime(ENOUGH_TIME - 1);
+
+ expect(checkForUpdatesMock).not.toHaveBeenCalled();
+ });
+
+ it("when just enough time passes, checks for updates again automatically", () => {
+ checkForUpdatesMock.mockClear();
+
+ jest.advanceTimersByTime(ENOUGH_TIME);
+
+ expect(checkForUpdatesMock).toHaveBeenCalled();
+ });
+ });
+
+ describe("given updater is enabled but no configuration exist, when started", () => {
+ beforeEach(async () => {
+ applicationBuilder.beforeApplicationStart(({ mainDi }) => {
+ mainDi.override(electronUpdaterIsActiveInjectable, () => true);
+ mainDi.override(publishIsConfiguredInjectable, () => false);
+ });
+
+ await applicationBuilder.render();
+ });
+
+ it("does not check for updates", () => {
+ expect(checkForUpdatesMock).not.toHaveBeenCalled();
+ });
+
+ it("when enough time passes for checking updates again, still does not check for updates", () => {
+ jest.advanceTimersByTime(ENOUGH_TIME);
+
+ expect(checkForUpdatesMock).not.toHaveBeenCalled();
+ });
+ });
+
+ describe("given updater is not enabled but and configuration exist, when started", () => {
+ beforeEach(async () => {
+ applicationBuilder.beforeApplicationStart(({ mainDi }) => {
+ mainDi.override(electronUpdaterIsActiveInjectable, () => false);
+ mainDi.override(publishIsConfiguredInjectable, () => true);
+ });
+
+ await applicationBuilder.render();
+ });
+
+ it("does not check for updates", () => {
+ expect(checkForUpdatesMock).not.toHaveBeenCalled();
+ });
+
+ it("when enough time passes for checking updates again, still does not check for updates", () => {
+ jest.advanceTimersByTime(ENOUGH_TIME);
+
+ expect(checkForUpdatesMock).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts
index e1ac3490ca..605b733ac2 100644
--- a/src/main/getDiForUnitTesting.ts
+++ b/src/main/getDiForUnitTesting.ts
@@ -84,6 +84,8 @@ import setUpdateOnQuitInjectable from "./electron-app/features/set-update-on-qui
import downloadPlatformUpdateInjectable from "./update-app/download-platform-update/download-platform-update.injectable";
import startCatalogSyncInjectable from "./catalog-sync-to-renderer/start-catalog-sync.injectable";
import startKubeConfigSyncInjectable from "./start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable";
+import startCheckingForUpdatesInjectable
+ from "./update-app/periodical-check-for-updates/start-checking-for-updates.injectable";
export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) {
const {
@@ -130,6 +132,8 @@ export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) {
di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} }));
+ di.override(startCheckingForUpdatesInjectable, () => ({ run: () => {} }));
+
// TODO: Remove usages of globally exported appEventBus to get rid of this
di.override(appEventBusInjectable, () => new EventEmitter<[AppEvent]>());
diff --git a/src/main/update-app/periodical-check-for-updates/periodical-check-for-updates.injectable.ts b/src/main/update-app/periodical-check-for-updates/periodical-check-for-updates.injectable.ts
new file mode 100644
index 0000000000..c973330030
--- /dev/null
+++ b/src/main/update-app/periodical-check-for-updates/periodical-check-for-updates.injectable.ts
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
+import checkForUpdatesInjectable from "../check-for-updates/check-for-updates.injectable";
+
+const periodicalCheckForUpdatesInjectable = getInjectable({
+ id: "periodical-check-for-updates",
+
+ instantiate: (di) => {
+ const checkForUpdates = di.inject(checkForUpdatesInjectable);
+
+ return getStartableStoppable("periodical-check-for-updates", () => {
+ const TWO_HOURS = 1000 * 60 * 60 * 2;
+
+ // Note: intentional orphan promise to make checking for updates happen in the background
+ checkForUpdates();
+
+ const intervalId = setInterval(() => {
+ // Note: intentional orphan promise to make checking for updates happen in the background
+ checkForUpdates();
+ }, TWO_HOURS);
+
+ return () => {
+ clearInterval(intervalId);
+ };
+ });
+ },
+});
+
+export default periodicalCheckForUpdatesInjectable;
diff --git a/src/main/update-app/periodical-check-for-updates/start-checking-for-updates.injectable.ts b/src/main/update-app/periodical-check-for-updates/start-checking-for-updates.injectable.ts
new file mode 100644
index 0000000000..9a9b9cf206
--- /dev/null
+++ b/src/main/update-app/periodical-check-for-updates/start-checking-for-updates.injectable.ts
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import periodicalCheckForUpdatesInjectable from "./periodical-check-for-updates.injectable";
+import { afterRootFrameIsReadyInjectionToken } from "../../start-main-application/runnable-tokens/after-root-frame-is-ready-injection-token";
+import updatingIsEnabledInjectable from "../updating-is-enabled.injectable";
+
+const startCheckingForUpdatesInjectable = getInjectable({
+ id: "start-checking-for-updates",
+
+ instantiate: (di) => {
+ const periodicalCheckForUpdates = di.inject(periodicalCheckForUpdatesInjectable);
+ const updatingIsEnabled = di.inject(updatingIsEnabledInjectable);
+
+ return {
+ run: async () => {
+ if (updatingIsEnabled) {
+ await periodicalCheckForUpdates.start();
+ }
+ },
+ };
+ },
+
+ injectionToken: afterRootFrameIsReadyInjectionToken,
+});
+
+export default startCheckingForUpdatesInjectable;
diff --git a/src/main/update-app/periodical-check-for-updates/stop-checking-for-updates.injectable.ts b/src/main/update-app/periodical-check-for-updates/stop-checking-for-updates.injectable.ts
new file mode 100644
index 0000000000..13aefe4d96
--- /dev/null
+++ b/src/main/update-app/periodical-check-for-updates/stop-checking-for-updates.injectable.ts
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) OpenLens Authors. All rights reserved.
+ * Licensed under MIT License. See LICENSE in root directory for more information.
+ */
+import { getInjectable } from "@ogre-tools/injectable";
+import { beforeQuitOfFrontEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-front-end-injection-token";
+import periodicalCheckForUpdatesInjectable from "./periodical-check-for-updates.injectable";
+
+const stopCheckingForUpdatesInjectable = getInjectable({
+ id: "stop-checking-for-updates",
+
+ instantiate: (di) => {
+ const periodicalCheckForUpdates = di.inject(periodicalCheckForUpdatesInjectable);
+
+ return {
+ run: async () => {
+ if (periodicalCheckForUpdates.started) {
+ await periodicalCheckForUpdates.stop();
+ }
+ },
+ };
+ },
+
+ injectionToken: beforeQuitOfFrontEndInjectionToken,
+});
+
+export default stopCheckingForUpdatesInjectable;