Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/vs/platform/update/common/update.config.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ configurationRegistry.registerConfiguration({
description: localize('updateMode', "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service."),
deprecationMessage: localize('deprecated', "This setting is deprecated, please use '{0}' instead.", 'update.mode')
},
'update.updateChannel': {
type: 'string',
enum: ['stable', 'beta', 'nightly'],
default: 'stable',
scope: ConfigurationScope.APPLICATION,
description: localize('updateChannel', "The update channel to use. 'stable' receives production releases, 'beta' receives pre-release versions, and 'nightly' receives daily builds. Requires a restart after change."),
tags: ['usesOnlineServices']
},
'update.enableWindowsBackgroundUpdates': {
type: 'boolean',
default: true,
Expand Down
2 changes: 2 additions & 0 deletions src/vs/platform/update/common/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface IUpdate {
timestamp?: number;
url?: string;
sha256hash?: string;
releaseNotes?: string; // URL to release notes
releaseNotesText?: string; // Release notes content (markdown or HTML)
}

/**
Expand Down
14 changes: 10 additions & 4 deletions src/vs/platform/update/electron-main/abstractUpdateService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import { IProductService } from '../../product/common/productService.js';
import { IRequestService } from '../../request/common/request.js';
import { AvailableForDownload, DisablementReason, IUpdateService, State, StateType, UpdateType } from '../common/update.js';

export function createUpdateURL(platform: string, quality: string, productService: IProductService): string {
return `${productService.updateUrl}/api/update/${platform}/${quality}/${productService.commit}`;
export function createUpdateURL(platform: string, quality: string, productService: IProductService, channel?: string): string {
// Use channel if provided, otherwise fall back to quality for backward compatibility
const channelOrQuality = channel || quality;
return `${productService.updateUrl}/api/update/${platform}/${channelOrQuality}/${productService.commit}`;
}

export type UpdateErrorClassification = {
Expand Down Expand Up @@ -89,7 +91,11 @@ export abstract class AbstractUpdateService implements IUpdateService {
return;
}

this.url = this.buildUpdateFeedUrl(quality);
// Get update channel from settings, default to 'stable' if not set
const updateChannel = this.configurationService.getValue<'stable' | 'beta' | 'nightly'>('update.updateChannel') || 'stable';
this.logService.info(`update#ctor - using update channel: ${updateChannel}`);

this.url = this.buildUpdateFeedUrl(quality, updateChannel);
if (!this.url) {
this.setState(State.Disabled(DisablementReason.InvalidConfiguration));
this.logService.info('update#ctor - updates are disabled as the update URL is badly formed');
Expand Down Expand Up @@ -230,6 +236,6 @@ export abstract class AbstractUpdateService implements IUpdateService {
// noop
}

protected abstract buildUpdateFeedUrl(quality: string): string | undefined;
protected abstract buildUpdateFeedUrl(quality: string, channel?: string): string | undefined;
protected abstract doCheckForUpdates(explicit: boolean): void;
}
4 changes: 2 additions & 2 deletions src/vs/platform/update/electron-main/updateService.darwin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau
this.setState(State.Idle(UpdateType.Archive, message));
}

protected buildUpdateFeedUrl(quality: string): string | undefined {
const url = createUpdateURL(process.platform, quality, this.productService);
protected buildUpdateFeedUrl(quality: string, channel?: string): string | undefined {
const url = createUpdateURL(process.platform, quality, this.productService, channel);
try {
electron.autoUpdater.setFeedURL({ url });
} catch (e) {
Expand Down
9 changes: 7 additions & 2 deletions src/vs/platform/update/electron-main/updateService.linux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export class LinuxUpdateService extends AbstractUpdateService {
super(lifecycleMainService, configurationService, environmentMainService, requestService, logService, productService);
}

protected buildUpdateFeedUrl(quality: string): string {
return createUpdateURL(`linux-${process.arch}`, quality, this.productService);
protected buildUpdateFeedUrl(quality: string, channel?: string): string {
return createUpdateURL(`linux-${process.arch}`, quality, this.productService, channel);
}

protected doCheckForUpdates(explicit: boolean): void {
Expand Down Expand Up @@ -58,6 +58,11 @@ export class LinuxUpdateService extends AbstractUpdateService {
}

protected override async doDownloadUpdate(state: AvailableForDownload): Promise<void> {
// Linux does not support automatic updates for most packaging formats (deb/rpm/AppImage).
// Only Snap packages support auto-updates via the system package manager.
// For other formats, we open the download page so users can manually download and install.
// This is intentional and truthful - we do not fake automatic updates.

// Use the download URL if available as we don't currently detect the package type that was
// installed and the website download page is more useful than the tarball generally.
if (this.productService.downloadUrl && this.productService.downloadUrl.length > 0) {
Expand Down
9 changes: 7 additions & 2 deletions src/vs/platform/update/electron-main/updateService.win32.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
await super.initialize();
}

protected buildUpdateFeedUrl(quality: string): string | undefined {
protected buildUpdateFeedUrl(quality: string, channel?: string): string | undefined {
let platform = `win32-${process.arch}`;

if (getUpdateType() === UpdateType.Archive) {
Expand All @@ -108,7 +108,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
platform += '-user';
}

return createUpdateURL(platform, quality, this.productService);
return createUpdateURL(platform, quality, this.productService, channel);
}

protected doCheckForUpdates(explicit: boolean): void {
Expand All @@ -129,6 +129,11 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
return Promise.resolve(null);
}

// Log release notes availability if present
if (update.releaseNotes) {
this.logService.info(`update#checkForUpdates - release notes available at: ${update.releaseNotes}`);
}

if (updateType === UpdateType.Archive) {
this.setState(State.AvailableForDownload(update));
return Promise.resolve(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { IAction } from '../../../../base/common/actions.js';


const notifyUpdate = (res: CortexideCheckUpdateResponse & { message: string }, notifService: INotificationService, updateService: IUpdateService): INotificationHandle => {
const message = res?.message || 'This is a very old version. Please download the latest CortexIDE!'
const message = res?.message || 'This is a very old version. Please download the latest CortexIDE!'

let actions: INotificationActions | undefined

Expand All @@ -37,7 +37,7 @@ const notifyUpdate = (res: CortexideCheckUpdateResponse & { message: string }, n
class: undefined,
run: () => {
const { window } = dom.getActiveWindow()
window.open('https://voideditor.com/download-beta')
window.open('https://opencortexide.com')
}
})
}
Expand Down Expand Up @@ -82,17 +82,17 @@ const notifyUpdate = (res: CortexideCheckUpdateResponse & { message: string }, n
})
}

primary.push({
id: 'void.updater.site',
enabled: true,
label: `CortexIDE Site`,
tooltip: '',
class: undefined,
run: () => {
const { window } = dom.getActiveWindow()
window.open('https://cortexide.com/')
}
})
primary.push({
id: 'void.updater.site',
enabled: true,
label: `CortexIDE Site`,
tooltip: '',
class: undefined,
run: () => {
const { window } = dom.getActiveWindow()
window.open('https://opencortexide.com')
}
})

actions = {
primary: primary,
Expand Down Expand Up @@ -127,7 +127,7 @@ const notifyUpdate = (res: CortexideCheckUpdateResponse & { message: string }, n
// })
}
const notifyErrChecking = (notifService: INotificationService): INotificationHandle => {
const message = `There was an error checking for updates. If this persists, please reinstall CortexIDE.`
const message = `There was an error checking for updates. If this persists, please reinstall CortexIDE.`
const notifController = notifService.notify({
severity: Severity.Info,
message: message,
Expand Down Expand Up @@ -177,7 +177,7 @@ registerAction2(class extends Action2 {
super({
f1: true,
id: 'void.voidCheckUpdate',
title: localize2('voidCheckUpdate', 'CortexIDE: Check for Updates'),
title: localize2('voidCheckUpdate', 'CortexIDE: Check for Updates'),
});
}
async run(accessor: ServicesAccessor): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/

import { CancellationToken } from '../../../../base/common/cancellation.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { IEnvironmentMainService } from '../../../../platform/environment/electron-main/environmentMainService.js';
import { IProductService } from '../../../../platform/product/common/productService.js';
import { asJson, IRequestService } from '../../../../platform/request/common/request.js';
import { IUpdateService, StateType } from '../../../../platform/update/common/update.js';
import { ICortexideUpdateService } from '../common/cortexideUpdateService.js';
import { CortexideCheckUpdateResponse } from '../common/cortexideUpdateServiceTypes.js';
Expand All @@ -19,6 +22,8 @@ export class CortexideMainUpdateService extends Disposable implements ICortexide
@IProductService private readonly _productService: IProductService,
@IEnvironmentMainService private readonly _envMainService: IEnvironmentMainService,
@IUpdateService private readonly _updateService: IUpdateService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IRequestService private readonly _requestService: IRequestService,
) {
super()
}
Expand All @@ -40,8 +45,6 @@ export class CortexideMainUpdateService extends Disposable implements ICortexide

this._updateService.checkForUpdates(false) // implicity check, then handle result ourselves

console.log('updateState', this._updateService.state)

if (this._updateService.state.type === StateType.Uninitialized) {
// The update service hasn't been initialized yet
return { message: explicit ? 'Checking for updates soon...' : null, action: explicit ? 'reinstall' : undefined } as const
Expand Down Expand Up @@ -83,7 +86,8 @@ export class CortexideMainUpdateService extends Disposable implements ICortexide
}

if (this._updateService.state.type === StateType.Disabled) {
return await this._manualCheckGHTagIfDisabled(explicit)
const channel = this._configurationService.getValue<'stable' | 'beta' | 'nightly'>('update.updateChannel') || 'stable';
return await this._manualCheckGHTagIfDisabled(explicit, channel)
}
return null
}
Expand All @@ -93,11 +97,31 @@ export class CortexideMainUpdateService extends Disposable implements ICortexide



private async _manualCheckGHTagIfDisabled(explicit: boolean): Promise<CortexideCheckUpdateResponse> {
private async _manualCheckGHTagIfDisabled(explicit: boolean, channel: 'stable' | 'beta' | 'nightly'): Promise<CortexideCheckUpdateResponse> {
try {
const response = await fetch('https://api.github.com/repos/cortexide/cortexide/releases/latest');
let releaseUrl: string;
if (channel === 'beta') {
releaseUrl = 'https://api.github.com/repos/OpenCortexIDE/cortexide/releases?per_page=1';
} else if (channel === 'nightly') {
releaseUrl = 'https://api.github.com/repos/OpenCortexIDE/cortexide/releases?per_page=1';
} else {
releaseUrl = 'https://api.github.com/repos/OpenCortexIDE/cortexide/releases/latest';
}

const context = await this._requestService.request({ url: releaseUrl, type: 'GET' }, CancellationToken.None);
if (context.res.statusCode !== 200) {
throw new Error(`GitHub API returned ${context.res.statusCode}`);
}

const jsonData = await asJson(context);
const data = channel === 'stable'
? jsonData
: Array.isArray(jsonData) ? jsonData[0] : jsonData;

if (!data || !data.tag_name) {
throw new Error('Invalid release data');
}

const data = await response.json();
const version = data.tag_name;

const myVersion = this._productService.version
Expand All @@ -110,23 +134,17 @@ export class CortexideMainUpdateService extends Disposable implements ICortexide

// explicit
if (explicit) {
if (response.ok) {
if (!isUpToDate) {
message = 'A new version of CortexIDE is available! Please reinstall (auto-updates are disabled on this OS) - it only takes a second!'
action = 'reinstall'
}
else {
message = 'CortexIDE is up-to-date!'
}
if (!isUpToDate) {
message = 'A new version of CortexIDE is available! Please reinstall (auto-updates are disabled on this OS) - it only takes a second!'
action = 'reinstall'
}
else {
message = `An error occurred when fetching the latest GitHub release tag. Please try again in ~5 minutes, or reinstall.`
action = 'reinstall'
message = 'CortexIDE is up-to-date!'
}
}
// not explicit
else {
if (response.ok && !isUpToDate) {
if (!isUpToDate) {
message = 'A new version of CortexIDE is available! Please reinstall (auto-updates are disabled on this OS) - it only takes a second!'
action = 'reinstall'
}
Expand Down
50 changes: 49 additions & 1 deletion src/vs/workbench/contrib/update/browser/update.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import product from '../../../../platform/product/common/product.js';
import { IUpdateService, StateType, State } from '../../../../platform/update/common/update.js';
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
import { isWindows, isWeb } from '../../../../base/common/platform.js';
import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js';
import { IFileDialogService, IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { INotificationService } from '../../../../platform/notification/common/notification.js';
import { mnemonicButtonLabel } from '../../../../base/common/labels.js';
import { ShowCurrentReleaseNotesActionId, ShowCurrentReleaseNotesFromCurrentFileActionId } from '../common/update.js';
import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js';
Expand Down Expand Up @@ -199,10 +201,56 @@ class DownloadAction extends Action2 {
}

registerAction2(DownloadAction);
class SwitchUpdateChannelAction extends Action2 {
constructor() {
super({
id: 'update.switchChannel',
title: localize2('switchUpdateChannel', 'Switch Update Channel...'),
category: { value: product.nameShort, original: product.nameShort },
f1: true,
});
}

async run(accessor: ServicesAccessor): Promise<void> {
const configurationService = accessor.get(IConfigurationService);
const dialogService = accessor.get(IDialogService);
const notificationService = accessor.get(INotificationService);

const currentChannel = configurationService.getValue<'stable' | 'beta' | 'nightly'>('update.updateChannel') || 'stable';

const { result } = await dialogService.prompt<'stable' | 'beta' | 'nightly'>({
type: 'info',
message: localize('switchUpdateChannel.message', 'Select Update Channel'),
detail: localize('switchUpdateChannel.detail', 'Choose which update channel to use. This will take effect after restart.'),
buttons: [
{
label: localize('updateChannel.stable', 'Stable'),
run: () => 'stable' as const
},
{
label: localize('updateChannel.beta', 'Beta'),
run: () => 'beta' as const
},
{
label: localize('updateChannel.nightly', 'Nightly'),
run: () => 'nightly' as const
}
],
cancelButton: true
});

if (result && result !== currentChannel) {
await configurationService.updateValue('update.updateChannel', result);
notificationService.info(localize('switchUpdateChannel.success', 'Update channel changed to {0}. Please restart for changes to take effect.', result));
}
}
}

registerAction2(CheckForUpdateAction);
registerAction2(DownloadUpdateAction);
registerAction2(InstallUpdateAction);
registerAction2(RestartToUpdateAction);
registerAction2(SwitchUpdateChannelAction);

if (isWindows) {
class DeveloperApplyUpdateAction extends Action2 {
Expand Down
Loading
Loading