Skip to content

Commit

Permalink
chore: add release notes to dashboard - main and preload (podman-desk…
Browse files Browse the repository at this point in the history
…top#8753)

* chore: add release notes to dashboard - main and preload
Signed-off-by: Sonia Sandler <[email protected]>
  • Loading branch information
SoniaSandler authored Oct 3, 2024
1 parent e7f2fa5 commit 9a8ea39
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 3 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "podman-desktop",
"productName": "Podman Desktop",
"repository": "https://github.com/containers/podman-desktop",
"homepage": "https://www.podman-desktop.io",
"version": "1.13.0-next",
"license": "apache-2.0",
"type": "module",
Expand Down
30 changes: 30 additions & 0 deletions packages/api/src/release-notes-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

export interface ReleaseNotes {
image: string;
blog: string;
title: string;
summary: string;
}

export interface ReleaseNotesInfo {
releaseNotesAvailable: boolean;
notesURL: string;
notes?: ReleaseNotes;
}
26 changes: 25 additions & 1 deletion packages/main/src/plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import type {
} from '/@api/provider-info.js';
import type { ProxyState } from '/@api/proxy.js';
import type { PullEvent } from '/@api/pull-event.js';
import type { ReleaseNotesInfo } from '/@api/release-notes-info.js';
import type { ViewInfoUI } from '/@api/view-info.js';
import type { VolumeInspectInfo, VolumeListInfo } from '/@api/volume-info.js';
import type { WebviewInfo } from '/@api/webview-info.js';
Expand Down Expand Up @@ -162,6 +163,7 @@ import { OpenDevToolsInit } from './open-devtools-init.js';
import { ProviderRegistry } from './provider-registry.js';
import { Proxy } from './proxy.js';
import { RecommendationsRegistry } from './recommendations/recommendations-registry.js';
import { ReleaseNotesBannerInit } from './release-notes-banner-init.js';
import { SafeStorageRegistry } from './safe-storage/safe-storage-registry.js';
import type { StatusBarEntryDescriptor } from './statusbar/statusbar-registry.js';
import { StatusBarRegistry } from './statusbar/statusbar-registry.js';
Expand Down Expand Up @@ -570,7 +572,14 @@ export class PluginSystem {
);

// Init update logic
new Updater(messageBox, configurationRegistry, statusBarRegistry, commandRegistry, taskManager).init();
const podmanDesktopUpdater = new Updater(
messageBox,
configurationRegistry,
statusBarRegistry,
commandRegistry,
taskManager,
);
podmanDesktopUpdater.init();

commandRegistry.registerCommand('feedback', () => {
apiSender.send('display-feedback', '');
Expand All @@ -591,6 +600,9 @@ export class PluginSystem {
const confirmationConfiguration = new ConfirmationInit(configurationRegistry);
confirmationConfiguration.init();

const releaseNotesBannerConfiguration = new ReleaseNotesBannerInit(configurationRegistry);
releaseNotesBannerConfiguration.init();

const terminalInit = new TerminalInit(configurationRegistry);
terminalInit.init();

Expand Down Expand Up @@ -1326,6 +1338,18 @@ export class PluginSystem {
},
);

this.ipcHandle('app:update', async (): Promise<void> => {
await commandRegistry.executeCommand('update');
});

this.ipcHandle('app:update-available', async (): Promise<boolean> => {
return podmanDesktopUpdater.updateAvailable();
});

this.ipcHandle('app:get-release-notes', async (): Promise<ReleaseNotesInfo> => {
return podmanDesktopUpdater.getReleaseNotes();
});

this.ipcHandle('provider-registry:getProviderInfos', async (): Promise<ProviderInfo[]> => {
return providerRegistry.getProviderInfos();
});
Expand Down
48 changes: 48 additions & 0 deletions packages/main/src/plugin/release-notes-banner-init.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import { expect, test, vi } from 'vitest';

import type { ConfigurationRegistry } from './configuration-registry.js';
import { ReleaseNotesBannerInit } from './release-notes-banner-init.js';

let releaseNotesBannerInit: ReleaseNotesBannerInit;

const registerConfigurationsMock = vi.fn();

const configurationRegistryMock = {
registerConfigurations: registerConfigurationsMock,
} as unknown as ConfigurationRegistry;

test('should register a configuration', async () => {
releaseNotesBannerInit = new ReleaseNotesBannerInit(configurationRegistryMock);

releaseNotesBannerInit.init();

expect(configurationRegistryMock.registerConfigurations).toBeCalled();

const configurationNode = vi.mocked(configurationRegistryMock.registerConfigurations).mock.calls[0]?.[0][0];
expect(configurationNode?.id).toBe('releaseNotesBanner');
expect(configurationNode?.title).toBe('Show release notes banner');
expect(configurationNode?.properties).toBeDefined();
expect(Object.keys(configurationNode?.properties ?? {}).length).toBe(1);
expect(configurationNode?.properties?.['releaseNotesBanner.show']).toBeDefined();
expect(configurationNode?.properties?.['releaseNotesBanner.show']?.type).toBe('string');
expect(configurationNode?.properties?.['releaseNotesBanner.show']?.default).toBe('show');
expect(configurationNode?.properties?.['releaseNotesBanner.show']?.hidden).toBe(true);
});
40 changes: 40 additions & 0 deletions packages/main/src/plugin/release-notes-banner-init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { IConfigurationNode, IConfigurationRegistry } from './configuration-registry.js';

export class ReleaseNotesBannerInit {
constructor(private configurationRegistry: IConfigurationRegistry) {}

init(): void {
const releaseNotesBannerConfiguration: IConfigurationNode = {
id: 'releaseNotesBanner',
title: 'Show release notes banner',
type: 'object',
properties: {
[`releaseNotesBanner.show`]: {
type: 'string',
default: 'show',
hidden: true,
},
},
};

this.configurationRegistry.registerConfigurations([releaseNotesBannerConfiguration]);
}
}
111 changes: 110 additions & 1 deletion packages/main/src/plugin/updater.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import type { IncomingMessage } from 'node:http';

import type { Configuration } from '@podman-desktop/api';
import { app } from 'electron';
import { app, shell } from 'electron';
import { type AppUpdater, autoUpdater, type UpdateCheckResult, type UpdateDownloadedEvent } from 'electron-updater';
import type { AppUpdaterEvents } from 'electron-updater/out/AppUpdater.js';
import { beforeEach, describe, expect, test, vi } from 'vitest';
Expand All @@ -38,6 +40,9 @@ vi.mock('electron', () => ({
app: {
getVersion: vi.fn(),
},
shell: {
openExternal: vi.fn(),
},
}));

vi.mock('electron-updater', () => ({
Expand All @@ -54,6 +59,19 @@ vi.mock('/@/util.js', () => ({
isLinux: vi.fn(),
}));

const getStatusCodeMock = { statusCode: 200 } as IncomingMessage;

vi.mock('https', () => ({
get: (_: string, callback: (_: IncomingMessage) => void): void => {
callback(getStatusCodeMock);
},
}));

vi.mock('../../../../package.json', () => ({
homepage: 'appHomepage',
repository: 'appRepo',
}));

const messageBoxMock = {
showMessageBox: vi.fn(),
} as unknown as MessageBox;
Expand Down Expand Up @@ -104,6 +122,7 @@ beforeEach(() => {
vi.mocked(taskManagerMock.createTask).mockResolvedValue({
progress: 0,
} as unknown as Task);
console.error = vi.fn();
});

test('expect env PROD to be truthy', () => {
Expand Down Expand Up @@ -539,3 +558,93 @@ describe('download task and progress', async () => {
downloadProgressCallback?.({ percent: 50 });
});
});

test('open release notes from podman-desktop.io', async () => {
vi.mocked(app.getVersion).mockReturnValue('1.1.0');
vi.mocked(autoUpdater.checkForUpdates).mockResolvedValue({
updateInfo: {
version: '1.2.0',
},
} as unknown as UpdateCheckResult);

const updater = new Updater(
messageBoxMock,
configurationRegistryMock,
statusBarRegistryMock,
commandRegistryMock,
taskManagerMock,
);

vi.mocked(shell.openExternal).mockResolvedValue();
updater.init();

await updater.openReleaseNotes('current');
expect(shell.openExternal).toBeCalledWith('appHomepage/blog/podman-desktop-release-1.1');
await updater.openReleaseNotes('latest');
expect(shell.openExternal).toBeCalledWith('appHomepage/blog/podman-desktop-release-1.2');
});

test('open release notes from GitHub', async () => {
vi.mocked(app.getVersion).mockReturnValue('0.20.0');
vi.mocked(autoUpdater.checkForUpdates).mockResolvedValue({
updateInfo: {
version: '0.21.0',
},
} as unknown as UpdateCheckResult);

getStatusCodeMock.statusCode = 404;

const updater = new Updater(
messageBoxMock,
configurationRegistryMock,
statusBarRegistryMock,
commandRegistryMock,
taskManagerMock,
);
vi.mocked(shell.openExternal).mockResolvedValue();
updater.init();

await updater.openReleaseNotes('current');
expect(shell.openExternal).toBeCalledWith('appRepo/releases/tag/v0.20.0');
await updater.openReleaseNotes('latest');
expect(shell.openExternal).toBeCalledWith('appRepo/releases/tag/v0.21.0');
});

test('get release notes', async () => {
const fetchJSONMock = vi.fn().mockResolvedValue({ data: 'some data' });
vi.spyOn(global, 'fetch').mockImplementation(() =>
Promise.resolve({ ok: true, json: fetchJSONMock } as unknown as Response),
);
vi.mocked(app.getVersion).mockReturnValue('1.1.0');

const updater = new Updater(
messageBoxMock,
configurationRegistryMock,
statusBarRegistryMock,
commandRegistryMock,
taskManagerMock,
);

updater.init();
let releaseNotes = await updater.getReleaseNotes();
expect(fetch).toBeCalledWith('appHomepage/release-notes/1.1.json');
expect(releaseNotes).toStrictEqual({
releaseNotesAvailable: true,
notesURL: 'appHomepage/blog/podman-desktop-release-1.1',
notes: { data: 'some data' },
});

vi.spyOn(global, 'fetch')
.mockResolvedValueOnce({ ok: false, json: fetchJSONMock.mockResolvedValue({}) } as unknown as Response)
.mockResolvedValueOnce({ ok: true, json: fetchJSONMock.mockResolvedValue({}) } as unknown as Response);

releaseNotes = await updater.getReleaseNotes();
expect(releaseNotes).toStrictEqual({ releaseNotesAvailable: false, notesURL: `appRepo/releases/tag/v1.1.0` });

vi.spyOn(global, 'fetch')
.mockResolvedValueOnce({ ok: false, json: fetchJSONMock.mockResolvedValue({}) } as unknown as Response)
.mockResolvedValueOnce({ ok: false, json: fetchJSONMock.mockResolvedValue({}) } as unknown as Response);

releaseNotes = await updater.getReleaseNotes();
expect(releaseNotes).toStrictEqual({ releaseNotesAvailable: false, notesURL: '' });
});
Loading

0 comments on commit 9a8ea39

Please sign in to comment.