Skip to content

Commit

Permalink
Add in product changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
mustard-mh committed Jul 20, 2022
1 parent b9743c9 commit f70e25a
Show file tree
Hide file tree
Showing 4 changed files with 335 additions and 0 deletions.
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"onCommand:gitpod.syncProvider.remove",
"onCommand:gitpod.exportLogs",
"onCommand:gitpod.api.autoTunnel",
"onCommand:gitpod.showReleaseNotes",
"onAuthenticationRequest:gitpod",
"onUri"
],
Expand Down Expand Up @@ -92,6 +93,11 @@
"command": "gitpod.exportLogs",
"category": "Gitpod",
"title": "Export all logs"
},
{
"command": "gitpod.showReleaseNotes",
"category": "Gitpod",
"title": "Show Release Notes"
}
]
},
Expand All @@ -109,6 +115,7 @@
"@types/analytics-node": "^3.1.9",
"@types/crypto-js": "4.1.1",
"@types/google-protobuf": "^3.7.4",
"@types/js-yaml": "^4.0.5",
"@types/node": "16.x",
"@types/node-fetch": "^2.5.12",
"@types/semver": "^7.3.10",
Expand All @@ -135,6 +142,7 @@
"@gitpod/local-app-api-grpcweb": "main",
"@improbable-eng/grpc-web-node-http-transport": "^0.14.0",
"analytics-node": "^6.0.0",
"js-yaml": "^4.1.0",
"node-fetch": "2.6.7",
"pkce-challenge": "^3.0.0",
"semver": "^7.3.7",
Expand Down
3 changes: 3 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { enableSettingsSync, updateSyncContext } from './settingsSync';
import { GitpodServer } from './gitpodServer';
import TelemetryReporter from './telemetryReporter';
import { exportLogs } from './exportLogs';
import { registerReleaseNotesView } from './releaseNotes';

const EXTENSION_ID = 'gitpod.gitpod-desktop';
const FIRST_INSTALL_KEY = 'gitpod-desktop.firstInstall';
Expand Down Expand Up @@ -89,6 +90,8 @@ export async function activate(context: vscode.ExtensionContext) {
await context.globalState.update(FIRST_INSTALL_KEY, true);
telemetry.sendTelemetryEvent('gitpod_desktop_installation', { kind: 'install' });
}

registerReleaseNotesView(context, logger);
}

export async function deactivate() {
Expand Down
319 changes: 319 additions & 0 deletions src/releaseNotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Gitpod. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import fetch from 'node-fetch';
import * as vscode from 'vscode';
import { load } from 'js-yaml';
import Log from './common/logger';

const LAST_READ_RELEASE_NOTES_ID = 'gitpod.lastReadReleaseNotesId';

export function registerReleaseNotesView(context: vscode.ExtensionContext, logger: Log) {

async function shouldShowReleaseNotes(lastReadId: string | undefined) {
const releaseId = await getLastPublish();
logger.info(`releaseNotes lastReadId: ${lastReadId}, latestReleaseId: ${releaseId}`);
return releaseId !== lastReadId;
}

context.subscriptions.push(
vscode.commands.registerCommand('gitpod.showReleaseNotes', () => {
ReleaseNotesPanel.createOrShow(context);
})
);

// sync between machines
context.globalState.setKeysForSync([LAST_READ_RELEASE_NOTES_ID]);

const lastReadId = context.globalState.get<string>(LAST_READ_RELEASE_NOTES_ID);
shouldShowReleaseNotes(lastReadId).then(shouldShow => {
if (shouldShow) {
ReleaseNotesPanel.createOrShow(context);
}
});
}

async function getLastPublish() {
const resp = await fetch(`${websiteHost}/changelog/latest`);
if (!resp.ok) {
throw new Error(`Getting latest releaseId failed: ${resp.statusText}`);
}
const { releaseId } = JSON.parse(await resp.text());
return releaseId as string;
}

const websiteHost = 'https://www.gitpod.io';

class ReleaseNotesPanel {
public static currentPanel: ReleaseNotesPanel | undefined;
public static readonly viewType = 'gitpodReleaseNotes';
private readonly panel: vscode.WebviewPanel;
private lastReadId: string | undefined;
private _disposables: vscode.Disposable[] = [];

private async loadChangelog(releaseId: string) {
const resp = await fetch(`${websiteHost}/changelog/raw-markdown?releaseId=${releaseId}`);
if (!resp.ok) {
throw new Error(`Getting raw markdown content failed: ${resp.statusText}`);
}
const md = await resp.text();

const parseInfo = (md: string) => {
if (!md.startsWith('---')) {
return;
}
const lines = md.split('\n');
const end = lines.indexOf('---', 1);
const content = lines.slice(1, end).join('\n');
return load(content) as { title: string; date: string; image: string; alt: string; excerpt: string };
};
const info = parseInfo(md);

const content = md
.replace(/---.*?---/gms, '')
.replace(/<script>.*?<\/script>/gms, '')
.replace(/<Badge.*?text="(.*?)".*?\/>/gim, '`$1`')
.replace(/<Contributors usernames="(.*?)" \/>/gim, (_, p1) => {
const users = p1
.split(',')
.map((e: string) => `[${e}](https://github.com/${e})`);
return `Contributors: ${users.join(', ')}`;
})
.replace(/<p>(.*?)<\/p>/gm, '$1')
.replace(/^[\n]+/m, '');
if (!info) {
return content;
}

return [
`# ${info.title}`,
`> Published at ${releaseId}, see also https://gitpod.io/changelog`,
`![${info.alt ?? 'image'}](https://www.gitpod.io/images/changelog/${info.image})`,
content,
].join('\n\n');
}

public async updateHtml(releaseId?: string) {
if (!releaseId) {
releaseId = await getLastPublish();
}
const mdContent = await this.loadChangelog(releaseId);
const html = await vscode.commands.executeCommand('markdown.api.render', mdContent) as string;
this.panel.webview.html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gitpod Release Notes</title>
<style>
${DEFAULT_MARKDOWN_STYLES}
</style>
</head>
<body>
${html}
</body>
</html>`;
if (!this.lastReadId || releaseId > this.lastReadId) {
await this.context.globalState.update(LAST_READ_RELEASE_NOTES_ID, releaseId);
this.lastReadId = releaseId;
}
}

public static createOrShow(context: vscode.ExtensionContext) {
const column = vscode.window.activeTextEditor
? vscode.window.activeTextEditor.viewColumn
: undefined;

if (ReleaseNotesPanel.currentPanel) {
ReleaseNotesPanel.currentPanel.panel.reveal(column);
return;
}

const panel = vscode.window.createWebviewPanel(
ReleaseNotesPanel.viewType,
'Gitpod Release Notes',
column || vscode.ViewColumn.One,
{ enableScripts: true },
);

ReleaseNotesPanel.currentPanel = new ReleaseNotesPanel(context, panel);
}

public static revive(context: vscode.ExtensionContext, panel: vscode.WebviewPanel) {
ReleaseNotesPanel.currentPanel = new ReleaseNotesPanel(context, panel);
}

private constructor(
private readonly context: vscode.ExtensionContext,
panel: vscode.WebviewPanel
) {
this.lastReadId = this.context.globalState.get<string>(LAST_READ_RELEASE_NOTES_ID);
this.panel = panel;

this.updateHtml();

this.panel.onDidDispose(() => this.dispose(), null, this._disposables);
this.panel.onDidChangeViewState(
() => {
if (this.panel.visible) {
this.updateHtml();
}
},
null,
this._disposables
);
}

public dispose() {
ReleaseNotesPanel.currentPanel = undefined;
this.panel.dispose();
while (this._disposables.length) {
const x = this._disposables.pop();
if (x) {
x.dispose();
}
}
}
}

// Align with https://github.com/gitpod-io/openvscode-server/blob/494f7eba3615344ee634e6bec0b20a1903e5881d/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts#L14
export const DEFAULT_MARKDOWN_STYLES = `
body {
padding: 10px 20px;
line-height: 22px;
max-width: 882px;
margin: 0 auto;
}
body *:last-child {
margin-bottom: 0;
}
img {
max-width: 100%;
max-height: 100%;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
hr {
border: 0;
height: 2px;
border-bottom: 2px solid;
}
h1 {
padding-bottom: 0.3em;
line-height: 1.2;
border-bottom-width: 1px;
border-bottom-style: solid;
}
h1, h2, h3 {
font-weight: normal;
}
table {
border-collapse: collapse;
}
table > thead > tr > th {
text-align: left;
border-bottom: 1px solid;
}
table > thead > tr > th,
table > thead > tr > td,
table > tbody > tr > th,
table > tbody > tr > td {
padding: 5px 10px;
}
table > tbody > tr + tr > td {
border-top-width: 1px;
border-top-style: solid;
}
blockquote {
margin: 0 7px 0 5px;
padding: 0 16px 0 10px;
border-left-width: 5px;
border-left-style: solid;
}
code {
font-family: "SF Mono", Monaco, Menlo, Consolas, "Ubuntu Mono", "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace;
}
pre code {
font-family: var(--vscode-editor-font-family);
font-weight: var(--vscode-editor-font-weight);
font-size: var(--vscode-editor-font-size);
line-height: 1.5;
}
code > div {
padding: 16px;
border-radius: 3px;
overflow: auto;
}
.monaco-tokenized-source {
white-space: pre;
}
/** Theming */
.vscode-light code > div {
background-color: rgba(220, 220, 220, 0.4);
}
.vscode-dark code > div {
background-color: rgba(10, 10, 10, 0.4);
}
.vscode-high-contrast code > div {
background-color: var(--vscode-textCodeBlock-background);
}
.vscode-high-contrast h1 {
border-color: rgb(0, 0, 0);
}
.vscode-light table > thead > tr > th {
border-color: rgba(0, 0, 0, 0.69);
}
.vscode-dark table > thead > tr > th {
border-color: rgba(255, 255, 255, 0.69);
}
.vscode-light h1,
.vscode-light hr,
.vscode-light table > tbody > tr + tr > td {
border-color: rgba(0, 0, 0, 0.18);
}
.vscode-dark h1,
.vscode-dark hr,
.vscode-dark table > tbody > tr + tr > td {
border-color: rgba(255, 255, 255, 0.18);
}
`;
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@
resolved "https://registry.yarnpkg.com/@types/google-protobuf/-/google-protobuf-3.15.6.tgz#674a69493ef2c849b95eafe69167ea59079eb504"
integrity sha512-pYVNNJ+winC4aek+lZp93sIKxnXt5qMkuKmaqS3WGuTq0Bw1ZDYNBgzG5kkdtwcv+GmYJGo3yEg6z2cKKAiEdw==

"@types/js-yaml@^4.0.5":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138"
integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==

"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.11"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
Expand Down

0 comments on commit f70e25a

Please sign in to comment.