提交 8cf4a447 编写于 作者: R rebornix

Merge remote-tracking branch 'origin/master' into rebornix/multi-exthost-renderers

......@@ -36,6 +36,17 @@ jobs:
steps:
- template: win32/product-build-win32.yml
- job: WindowsARM64
condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true'))
pool:
vmImage: VS2017-Win2016
variables:
VSCODE_ARCH: arm64
dependsOn:
- Compile
steps:
- template: win32/product-build-win32-arm64.yml
- job: Linux
condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true'))
pool:
......
......@@ -72,29 +72,6 @@ steps:
vstsFeed: 'npm-vscode'
condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true'))
- script: |
set -e
yarn generate-github-config
displayName: Generate GitHub config
env:
OSS_GITHUB_ID: "a5d3c261b032765a78de"
OSS_GITHUB_SECRET: $(oss-github-client-secret)
INSIDERS_GITHUB_ID: "31f02627809389d9f111"
INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret)
STABLE_GITHUB_ID: "baa8a44b5e861d918709"
STABLE_GITHUB_SECRET: $(stable-github-client-secret)
EXPLORATION_GITHUB_ID: "94e8376d3a90429aeaea"
EXPLORATION_GITHUB_SECRET: $(exploration-github-client-secret)
VSO_GITHUB_ID: "3d4be8f37a0325b5817d"
VSO_GITHUB_SECRET: $(vso-github-client-secret)
VSO_PPE_GITHUB_ID: "eabf35024dc2e891a492"
VSO_PPE_GITHUB_SECRET: $(vso-ppe-github-client-secret)
VSO_DEV_GITHUB_ID: "84383ebd8a7c5f5efc5c"
VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret)
GITHUB_APP_ID: "Iv1.ae51e546bef24ff1"
GITHUB_APP_SECRET: $(github-app-client-secret)
condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true'))
- script: |
set -e
yarn postinstall
......
steps:
- powershell: |
mkdir .build -ea 0
"$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit
"$env:VSCODE_QUALITY" | Out-File -Encoding ascii -NoNewLine .build\quality
displayName: Prepare cache flag
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
inputs:
keyfile: 'build/.cachesalt, .build/commit, .build/quality'
targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min'
vstsFeed: 'npm-vscode'
platformIndependent: true
alias: 'Compilation'
- powershell: |
$ErrorActionPreference = "Stop"
exit 1
displayName: Check RestoreCache
condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'))
- task: NodeTool@0
inputs:
versionSpec: "12.13.0"
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
inputs:
versionSpec: "1.x"
- task: UsePythonVersion@0
inputs:
versionSpec: '2.x'
addToPath: true
- task: AzureKeyVault@1
displayName: 'Azure Key Vault: Get Secrets'
inputs:
azureSubscription: 'vscode-builds-subscription'
KeyVaultName: vscode
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
"machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII
exec { git config user.email "vscode@microsoft.com" }
exec { git config user.name "VSCode" }
mkdir .build -ea 0
"$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch
displayName: Prepare tooling
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" }
exec { git fetch distro }
exec { git merge $(node -p "require('./package.json').distro") }
displayName: Merge distro
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
inputs:
keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
vstsFeed: 'npm-vscode'
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
$env:npm_config_arch="$(VSCODE_ARCH)"
$env:CHILD_CONCURRENCY="1"
exec { yarn --frozen-lockfile }
displayName: Install dependencies
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
inputs:
keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
vstsFeed: 'npm-vscode'
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { yarn postinstall }
displayName: Run postinstall scripts
condition: and(succeeded(), eq(variables['CacheRestored'], 'true'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { node build/azure-pipelines/mixin }
displayName: Mix in quality
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
$env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)"
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min-ci" }
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-code-helper" }
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" }
displayName: Build
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
inputs:
ConnectedServiceName: 'ESRP CodeSign'
FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)'
Pattern: '*.dll,*.exe,*.node'
signConfigType: inlineSignParams
inlineOperation: |
[
{
"keyCode": "CP-230012",
"operationSetCode": "SigntoolSign",
"parameters": [
{
"parameterName": "OpusName",
"parameterValue": "VS Code"
},
{
"parameterName": "OpusInfo",
"parameterValue": "https://code.visualstudio.com/"
},
{
"parameterName": "Append",
"parameterValue": "/as"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"toolName": "sign",
"toolVersion": "1.0"
},
{
"keyCode": "CP-230012",
"operationSetCode": "SigntoolVerify",
"parameters": [
{
"parameterName": "VerifyAll",
"parameterValue": "/all"
}
],
"toolName": "sign",
"toolVersion": "1.0"
}
]
SessionTimeout: 120
- task: NuGetCommand@2
displayName: Install ESRPClient.exe
inputs:
restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config'
feedsToUse: config
nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config'
externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b
restoreDirectory: packages
- task: ESRPImportCertTask@1
displayName: Import ESRP Request Signing Certificate
inputs:
ESRP: 'ESRP CodeSign'
- powershell: |
$ErrorActionPreference = "Stop"
.\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(esrp-auth-certificate) -AuthCertificateKey $(esrp-auth-certificate-key)
displayName: Import ESRP Auth Certificate
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
$env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)"
$env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)"
$env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)"
.\build\azure-pipelines\win32\publish.ps1
displayName: Publish
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: 'Component Detection'
continueOnError: true
......@@ -16,16 +16,21 @@ $ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip"
$Build = "$Root\VSCode-win32-$Arch"
# Create server archive
exec { xcopy $LegacyServer $Server /H /E /I }
exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r }
if ("$Arch" -ne "arm64") {
exec { xcopy $LegacyServer $Server /H /E /I }
exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r }
}
# get version
$PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json
$Version = $PackageJson.version
$AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-x64" }
$AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-$Arch" }
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform-archive" archive "VSCode-win32-$Arch-$Version.zip" $Zip }
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform" setup "VSCodeSetup-$Arch-$Version.exe" $SystemExe }
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform-user" setup "VSCodeUserSetup-$Arch-$Version.exe" $UserExe }
exec { node build/azure-pipelines/common/createAsset.js "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $ServerZip }
if ("$Arch" -ne "arm64") {
exec { node build/azure-pipelines/common/createAsset.js "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $ServerZip }
}
......@@ -65,6 +65,7 @@ function buildWin32Setup(arch, target) {
return cb => {
const ia32AppId = target === 'system' ? product.win32AppId : product.win32UserAppId;
const x64AppId = target === 'system' ? product.win32x64AppId : product.win32x64UserAppId;
const arm64AppId = target === 'system' ? product.win32arm64AppId : product.win32arm64UserAppId;
const sourcePath = buildPath(arch);
const outputPath = setupDir(arch, target);
......@@ -88,12 +89,12 @@ function buildWin32Setup(arch, target) {
ShellNameShort: product.win32ShellNameShort,
AppMutex: product.win32MutexName,
Arch: arch,
AppId: arch === 'ia32' ? ia32AppId : x64AppId,
IncompatibleTargetAppId: arch === 'ia32' ? product.win32AppId : product.win32x64AppId,
IncompatibleArchAppId: arch === 'ia32' ? x64AppId : ia32AppId,
AppId: { 'ia32': ia32AppId, 'x64': x64AppId, 'arm64': arm64AppId }[arch],
IncompatibleTargetAppId: { 'ia32': product.win32AppId, 'x64': product.win32x64AppId, 'arm64': product.win32arm64AppId }[arch],
IncompatibleArchAppId: { 'ia32': x64AppId, 'x64': ia32AppId, 'arm64': ia32AppId }[arch],
AppUserId: product.win32AppUserModelId,
ArchitecturesAllowed: arch === 'ia32' ? '' : 'x64',
ArchitecturesInstallIn64BitMode: arch === 'ia32' ? '' : 'x64',
ArchitecturesAllowed: { 'ia32': '', 'x64': 'x64', 'arm64': '' }[arch],
ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': '' }[arch],
SourceDir: sourcePath,
RepoDir: repoPath,
OutputDir: outputPath,
......@@ -112,8 +113,10 @@ function defineWin32SetupTasks(arch, target) {
defineWin32SetupTasks('ia32', 'system');
defineWin32SetupTasks('x64', 'system');
defineWin32SetupTasks('arm64', 'system');
defineWin32SetupTasks('ia32', 'user');
defineWin32SetupTasks('x64', 'user');
defineWin32SetupTasks('arm64', 'user');
function archiveWin32Setup(arch) {
return cb => {
......@@ -145,6 +148,7 @@ function updateIcon(executablePath) {
gulp.task(task.define('vscode-win32-ia32-inno-updater', task.series(copyInnoUpdater('ia32'), updateIcon(path.join(buildPath('ia32'), 'tools', 'inno_updater.exe')))));
gulp.task(task.define('vscode-win32-x64-inno-updater', task.series(copyInnoUpdater('x64'), updateIcon(path.join(buildPath('x64'), 'tools', 'inno_updater.exe')))));
gulp.task(task.define('vscode-win32-arm64-inno-updater', task.series(copyInnoUpdater('arm64'), updateIcon(path.join(buildPath('arm64'), 'tools', 'inno_updater.exe')))));
// CodeHelper.exe icon
......
......@@ -33,9 +33,10 @@ function yarnInstall(location, opts) {
yarnInstall('extensions'); // node modules shared by all extensions
yarnInstall('remote'); // node modules used by vscode server
yarnInstall('remote/web'); // node modules used by vscode web
if (!(process.platform === 'win32' && process.env['npm_config_arch'] === 'arm64')) {
yarnInstall('remote'); // node modules used by vscode server
yarnInstall('remote/web'); // node modules used by vscode web
}
const allExtensionFolders = fs.readdirSync('extensions');
const extensions = allExtensionFolders.filter(e => {
......
......@@ -1018,7 +1018,7 @@ begin
#endif
#if "user" == InstallTarget
#if "ia32" == Arch
#if "ia32" == Arch || "arm64" == Arch
#define IncompatibleArchRootKey "HKLM32"
#else
#define IncompatibleArchRootKey "HKLM64"
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const fs = require('fs');
const path = require('path');
const schemes = ['OSS', 'INSIDERS', 'STABLE', 'EXPLORATION', 'VSO', 'VSO_PPE', 'VSO_DEV'];
function main() {
let content = {};
for (const scheme of schemes) {
const id = process.env[`${scheme}_GITHUB_ID`];
const secret = process.env[`${scheme}_GITHUB_SECRET`];
if (id && secret) {
content[scheme] = { id, secret };
}
}
const githubAppId = process.env.GITHUB_APP_ID;
const githubAppSecret = process.env.GITHUB_APP_SECRET;
if (githubAppId && githubAppSecret) {
content.GITHUB_APP = { id: githubAppId, secret: githubAppSecret }
}
if (Object.keys(content).length > 0) {
fs.writeFileSync(path.join(__dirname, '../src/common/config.json'), JSON.stringify(content));
}
}
main();
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Uri, env } from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
export interface ClientDetails {
id?: string;
secret?: string;
}
export interface ClientConfig {
OSS: ClientDetails;
INSIDERS: ClientDetails;
STABLE: ClientDetails;
EXPLORATION: ClientDetails;
VSO: ClientDetails;
VSO_PPE: ClientDetails;
VSO_DEV: ClientDetails;
GITHUB_APP: ClientDetails;
}
export class Registrar {
private _config: ClientConfig;
constructor() {
try {
const fileContents = fs.readFileSync(path.join(env.appRoot, 'extensions/github-authentication/src/common/config.json')).toString();
this._config = JSON.parse(fileContents);
} catch (e) {
this._config = {
OSS: {},
INSIDERS: {},
STABLE: {},
EXPLORATION: {},
VSO: {},
VSO_PPE: {},
VSO_DEV: {},
GITHUB_APP: {}
};
}
}
getGitHubAppDetails(): ClientDetails {
if (!this._config.GITHUB_APP.id || !this._config.GITHUB_APP.secret) {
throw new Error(`No GitHub App client configuration available`);
}
return this._config.GITHUB_APP;
}
getClientDetails(callbackUri: Uri): ClientDetails {
let details: ClientDetails | undefined;
switch (callbackUri.scheme) {
case 'code-oss':
details = this._config.OSS;
break;
case 'vscode-insiders':
details = this._config.INSIDERS;
break;
case 'vscode':
details = this._config.STABLE;
break;
case 'vscode-exploration':
details = this._config.EXPLORATION;
break;
case 'https':
switch (callbackUri.authority) {
case 'online.visualstudio.com':
details = this._config.VSO;
break;
case 'online-ppe.core.vsengsaas.visualstudio.com':
details = this._config.VSO_PPE;
break;
case 'online.dev.core.vsengsaas.visualstudio.com':
details = this._config.VSO_DEV;
break;
}
default:
throw new Error(`Unrecognized callback ${callbackUri}`);
}
if (!details.id || !details.secret) {
throw new Error(`No client configuration available for ${callbackUri}`);
}
return details;
}
}
const ClientRegistrar = new Registrar();
export default ClientRegistrar;
......@@ -44,8 +44,6 @@ export class GitHubAuthenticationProvider {
// Ignore, network request failed
}
// TODO revert Cannot validate tokens from auth server, no available clientId
// await this.validateSessions();
this.pollForChange();
}
......@@ -144,22 +142,12 @@ export class GitHubAuthenticationProvider {
}
public async login(scopes: string): Promise<vscode.AuthenticationSession2> {
const token = scopes === 'vso' ? await this.loginAndInstallApp(scopes) : await this._githubServer.login(scopes);
const token = await this._githubServer.login(scopes);
const session = await this.tokenToSession(token, scopes.split(' '));
await this.setToken(session);
return session;
}
public async loginAndInstallApp(scopes: string): Promise<string> {
const token = await this._githubServer.login(scopes);
const hasUserInstallation = await this._githubServer.hasUserInstallation(token);
if (hasUserInstallation) {
return token;
} else {
return this._githubServer.installApp();
}
}
public async manuallyProvideToken(): Promise<void> {
this._githubServer.manuallyProvideToken();
}
......@@ -184,14 +172,6 @@ export class GitHubAuthenticationProvider {
const sessionIndex = this._sessions.findIndex(session => session.id === id);
if (sessionIndex > -1) {
this._sessions.splice(sessionIndex, 1);
// TODO revert
// Cannot revoke tokens from auth server, no clientId available
// const token = await session.getAccessToken();
// try {
// await this._githubServer.revokeToken(token);
// } catch (_) {
// // ignore, should still remove from keychain
// }
}
await this.storeSessions();
......
......@@ -9,7 +9,6 @@ import * as vscode from 'vscode';
import * as uuid from 'uuid';
import { PromiseAdapter, promiseFromEvent } from './common/utils';
import Logger from './common/logger';
import ClientRegistrar from './common/clientRegistrar';
const localize = nls.loadMessageBundle();
......@@ -24,8 +23,8 @@ class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.
export const uriHandler = new UriEventHandler;
const exchangeCodeForToken: (state: string, host: string, getPath: (code: string) => string) => PromiseAdapter<vscode.Uri, string> =
(state, host, getPath) => async (uri, resolve, reject) => {
const exchangeCodeForToken: (state: string) => PromiseAdapter<vscode.Uri, string> =
(state) => async (uri, resolve, reject) => {
Logger.info('Exchanging code for token...');
const query = parseQuery(uri);
const code = query.code;
......@@ -36,8 +35,8 @@ const exchangeCodeForToken: (state: string, host: string, getPath: (code: string
}
const post = https.request({
host: host,
path: getPath(code),
host: AUTH_RELAY_SERVER,
path: `/token?code=${code}&state=${state}`,
method: 'POST',
headers: {
Accept: 'application/json'
......@@ -81,26 +80,13 @@ export class GitHubServer {
const state = uuid();
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
let uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code`);
if (scopes === 'vso') {
const clientDetails = ClientRegistrar.getGitHubAppDetails();
uri = vscode.Uri.parse(`https://github.com/login/oauth/authorize?redirect_uri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&client_id=${clientDetails.id}`);
}
const uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code`);
vscode.env.openExternal(uri);
return promiseFromEvent(uriHandler.event, exchangeCodeForToken(state,
scopes === 'vso' ? 'github.com' : AUTH_RELAY_SERVER,
(code) => {
if (scopes === 'vso') {
const clientDetails = ClientRegistrar.getGitHubAppDetails();
return `/login/oauth/access_token?client_id=${clientDetails.id}&client_secret=${clientDetails.secret}&state=${state}&code=${code}`;
} else {
return `/token?code=${code}&state=${state}`;
}
})).finally(() => {
this.updateStatusBarItem(false);
});
return promiseFromEvent(uriHandler.event, exchangeCodeForToken(state)).finally(() => {
this.updateStatusBarItem(false);
});
}
private updateStatusBarItem(isStart?: boolean) {
......@@ -130,51 +116,6 @@ export class GitHubServer {
}
}
public async hasUserInstallation(token: string): Promise<boolean> {
return new Promise((resolve, reject) => {
Logger.info('Getting user installations...');
const post = https.request({
host: 'api.github.com',
path: `/user/installations`,
method: 'GET',
headers: {
Accept: 'application/vnd.github.machine-man-preview+json',
Authorization: `token ${token}`,
'User-Agent': 'Visual-Studio-Code'
}
}, result => {
const buffer: Buffer[] = [];
result.on('data', (chunk: Buffer) => {
buffer.push(chunk);
});
result.on('end', () => {
if (result.statusCode === 200) {
const json = JSON.parse(Buffer.concat(buffer).toString());
Logger.info('Got installation info!');
const hasInstallation = json.installations.some((installation: { app_slug: string }) => installation.app_slug === 'microsoft-visual-studio-code');
resolve(hasInstallation);
} else {
reject(new Error(result.statusMessage));
}
});
});
post.end();
post.on('error', err => {
reject(err);
});
});
}
public async installApp(): Promise<string> {
const clientDetails = ClientRegistrar.getGitHubAppDetails();
const state = uuid();
const uri = vscode.Uri.parse(`https://github.com/apps/microsoft-visual-studio-code/installations/new?state=${state}`);
vscode.env.openExternal(uri);
return promiseFromEvent(uriHandler.event, exchangeCodeForToken(state, 'github.com', (code) => `/login/oauth/access_token?client_id=${clientDetails.id}&client_secret=${clientDetails.secret}&state=${state}&code=${code}`));
}
public async getUserInfo(token: string): Promise<{ id: string, accountName: string }> {
return new Promise((resolve, reject) => {
Logger.info('Getting account info...');
......@@ -210,91 +151,4 @@ export class GitHubServer {
});
});
}
public async validateToken(token: string): Promise<void> {
return new Promise(async (resolve, reject) => {
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
const clientDetails = ClientRegistrar.getClientDetails(callbackUri);
const detailsString = `${clientDetails.id}:${clientDetails.secret}`;
const payload = JSON.stringify({ access_token: token });
Logger.info('Validating token...');
const post = https.request({
host: 'api.github.com',
path: `/applications/${clientDetails.id}/token`,
method: 'POST',
headers: {
Authorization: `Basic ${Buffer.from(detailsString).toString('base64')}`,
'User-Agent': 'Visual-Studio-Code',
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload)
}
}, result => {
const buffer: Buffer[] = [];
result.on('data', (chunk: Buffer) => {
buffer.push(chunk);
});
result.on('end', () => {
if (result.statusCode === 200) {
Logger.info('Validated token!');
resolve();
} else {
Logger.info(`Validating token failed: ${result.statusMessage}`);
reject(new Error(result.statusMessage));
}
});
});
post.write(payload);
post.end();
post.on('error', err => {
Logger.error(err.message);
reject(new Error(NETWORK_ERROR));
});
});
}
public async revokeToken(token: string): Promise<void> {
return new Promise(async (resolve, reject) => {
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
const clientDetails = ClientRegistrar.getClientDetails(callbackUri);
const detailsString = `${clientDetails.id}:${clientDetails.secret}`;
const payload = JSON.stringify({ access_token: token });
Logger.info('Revoking token...');
const post = https.request({
host: 'api.github.com',
path: `/applications/${clientDetails.id}/token`,
method: 'DELETE',
headers: {
Authorization: `Basic ${Buffer.from(detailsString).toString('base64')}`,
'User-Agent': 'Visual-Studio-Code',
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload)
}
}, result => {
const buffer: Buffer[] = [];
result.on('data', (chunk: Buffer) => {
buffer.push(chunk);
});
result.on('end', () => {
if (result.statusCode === 204) {
Logger.info('Revoked token!');
resolve();
} else {
Logger.info(`Revoking token failed: ${result.statusMessage}`);
reject(new Error(result.statusMessage));
}
});
});
post.write(payload);
post.end();
post.on('error', err => {
reject(err);
});
});
}
}
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.32798 5.00905L1.2384 8.09746L4.32798 11.1859L3.5016 12.0123L0 8.51065V7.68427L3.5016 4.18267L4.32798 5.00905ZM12.4984 4.18267L11.672 5.00905L14.7616 8.09746L11.672 11.1859L12.4984 12.0123L16 8.51065V7.68427L12.4984 4.18267ZM4.56142 13.672L5.6049 14.1949L11.4409 2.52291L10.3974 2L4.56142 13.672V13.672Z" fill="black"/>
</svg>
......@@ -55,7 +55,8 @@
{
"id": "npm",
"name": "%view.name%",
"when": "npm:showScriptExplorer || config.npm.enableScriptExplorer"
"when": "npm:showScriptExplorer || config.npm.enableScriptExplorer",
"icon": "images/code.svg"
}
]
},
......
{
"name": "code-oss-dev",
"version": "1.46.0",
"distro": "64a6a85a3b1af4e177210fa8c72b822322b129ab",
"distro": "4eb4d754206e598d49d6bd0d3a1a6f0cddda92a7",
"author": {
"name": "Microsoft Corporation"
},
......@@ -33,8 +33,7 @@
"strict-function-types-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictFunctionTypes",
"update-distro": "node build/npm/update-distro.js",
"web": "node scripts/code-web.js",
"eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions",
"generate-github-config": "node extensions/github-authentication/build/generateconfig.js"
"eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions"
},
"dependencies": {
"applicationinsights": "1.0.8",
......@@ -182,4 +181,4 @@
"windows-mutex": "0.3.0",
"windows-process-tree": "0.2.4"
}
}
}
\ No newline at end of file
......@@ -11,8 +11,10 @@
"win32RegValueName": "CodeOSS",
"win32AppId": "{{E34003BB-9E10-4501-8C11-BE3FAA83F23F}",
"win32x64AppId": "{{D77B7E06-80BA-4137-BCF4-654B95CCEBC5}",
"win32arm64AppId": "{{D1ACE434-89C5-48D1-88D3-E2991DF85475}",
"win32UserAppId": "{{C6065F05-9603-4FC4-8101-B9781A25D88E}",
"win32x64UserAppId": "{{C6065F05-9603-4FC4-8101-B9781A25D88E}",
"win32arm64UserAppId": "{{3AEBF0C8-F733-4AD4-BADE-FDB816D53D7B}",
"win32AppUserModelId": "Microsoft.CodeOSS",
"win32ShellNameShort": "C&ode - OSS",
"darwinBundleIdentifier": "com.visualstudio.code.oss",
......
......@@ -92,7 +92,8 @@ export interface IGalleryMetadata {
export interface ILocalExtension extends IExtension {
readonly manifest: IExtensionManifest;
metadata: IGalleryMetadata;
publisherId: string | null;
publisherDisplayName: string | null;
readmeUrl: URI | null;
changelogUrl: URI | null;
}
......
......@@ -69,9 +69,9 @@ export function getLocalExtensionTelemetryData(extension: ILocalExtension): any
id: extension.identifier.id,
name: extension.manifest.name,
galleryId: null,
publisherId: extension.metadata ? extension.metadata.publisherId : null,
publisherId: extension.publisherId,
publisherName: extension.manifest.publisher,
publisherDisplayName: extension.metadata ? extension.metadata.publisherDisplayName : null,
publisherDisplayName: extension.publisherDisplayName,
dependencies: extension.manifest.extensionDependencies && extension.manifest.extensionDependencies.length > 0
};
}
......@@ -116,4 +116,4 @@ export function getMaliciousExtensionsSet(report: IReportedExtension[]): Set<str
}
return result;
}
\ No newline at end of file
}
......@@ -419,8 +419,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
let local = await this.extensionsScanner.extractUserExtension(identifierWithVersion, zipPath, token);
this.logService.info('Installation completed.', identifier.id);
if (metadata) {
this.setMetadata(local, metadata);
local = await this.extensionsScanner.saveMetadataForLocalExtension(local);
local = await this.extensionsScanner.saveMetadataForLocalExtension(local, metadata);
}
return local;
}
......@@ -481,8 +480,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension> {
this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id);
local.metadata = metadata;
local = await this.extensionsScanner.saveMetadataForLocalExtension(local);
local = await this.extensionsScanner.saveMetadataForLocalExtension(local, metadata);
this.manifestCache.invalidate();
return local;
}
......@@ -629,11 +627,6 @@ export class ExtensionManagementService extends Disposable implements IExtension
return this.extensionsScanner.scanExtensions(type);
}
private setMetadata(local: ILocalExtension, metadata: IGalleryMetadata): void {
local.metadata = metadata;
local.identifier.uuid = metadata.id;
}
removeDeprecatedExtensions(): Promise<void> {
return this.extensionsScanner.cleanUp();
}
......
......@@ -127,14 +127,13 @@ export class ExtensionsScanner extends Disposable {
throw new Error(localize('cannot read', "Cannot read the extension from {0}", this.extensionsPath));
}
async saveMetadataForLocalExtension(local: ILocalExtension): Promise<ILocalExtension> {
if (local.metadata) {
const manifestPath = path.join(local.location.fsPath, 'package.json');
const raw = await pfs.readFile(manifestPath, 'utf8');
const { manifest } = await this.parseManifest(raw);
assign(manifest, { __metadata: local.metadata });
await pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t'));
}
async saveMetadataForLocalExtension(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension> {
this.setMetadata(local, metadata);
const manifestPath = path.join(local.location.fsPath, 'package.json');
const raw = await pfs.readFile(manifestPath, 'utf8');
const { manifest } = await this.parseManifest(raw);
assign(manifest, { __metadata: metadata });
await pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t'));
return local;
}
......@@ -228,7 +227,7 @@ export class ExtensionsScanner extends Disposable {
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)) : null;
const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };
const local = <ILocalExtension>{ type, identifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl };
const local = <ILocalExtension>{ type, identifier, manifest, location: URI.file(extensionPath), readmeUrl, changelogUrl, publisherDisplayName: null, publisherId: null };
if (metadata) {
this.setMetadata(local, metadata);
}
......@@ -257,7 +256,8 @@ export class ExtensionsScanner extends Disposable {
}
private setMetadata(local: ILocalExtension, metadata: IGalleryMetadata): void {
local.metadata = metadata;
local.publisherDisplayName = metadata.publisherDisplayName;
local.publisherId = metadata.publisherId;
local.identifier.uuid = metadata.id;
}
......@@ -314,7 +314,7 @@ export class ExtensionsScanner extends Disposable {
return this._devSystemExtensionsPath;
}
private async readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata; }> {
private async readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata | null; }> {
const promises = [
pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8')
.then(raw => this.parseManifest(raw)),
......@@ -330,7 +330,7 @@ export class ExtensionsScanner extends Disposable {
};
}
private parseManifest(raw: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata; }> {
private parseManifest(raw: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata | null; }> {
return new Promise((c, e) => {
try {
const manifest = JSON.parse(raw);
......
......@@ -157,6 +157,7 @@ class ResourceEditStack {
private _past: StackElement[];
private _future: StackElement[];
public locked: boolean;
public versionId: number;
constructor(resource: URI, strResource: string) {
this.resource = resource;
......@@ -164,6 +165,7 @@ class ResourceEditStack {
this._past = [];
this._future = [];
this.locked = false;
this.versionId = 1;
}
public dispose(): void {
......@@ -177,11 +179,13 @@ class ResourceEditStack {
element.removeResource(this.resource, this.strResource, RemovedResourceReason.ExternalRemoval);
}
}
this.versionId++;
}
public flushAllElements(): void {
this._past = [];
this._future = [];
this.versionId++;
}
public setElementsIsValid(isValid: boolean): void {
......@@ -217,6 +221,7 @@ class ResourceEditStack {
}
}
this._past.push(element);
this.versionId++;
}
public getElements(): IPastFutureElements {
......@@ -268,6 +273,7 @@ class ResourceEditStack {
break;
}
}
this.versionId++;
}
public splitFutureWorkspaceElement(toRemove: WorkspaceStackElement, individualMap: Map<string, ResourceStackElement>): void {
......@@ -283,16 +289,42 @@ class ResourceEditStack {
break;
}
}
this.versionId++;
}
public moveBackward(element: StackElement): void {
this._past.pop();
this._future.push(element);
this.versionId++;
}
public moveForward(element: StackElement): void {
this._future.pop();
this._past.push(element);
this.versionId++;
}
}
class EditStackSnapshot {
public readonly editStacks: ResourceEditStack[];
private readonly _versionIds: number[];
constructor(editStacks: ResourceEditStack[]) {
this.editStacks = editStacks;
this._versionIds = [];
for (let i = 0, len = this.editStacks.length; i < len; i++) {
this._versionIds[i] = this.editStacks[i].versionId;
}
}
public isValid(): boolean {
for (let i = 0, len = this.editStacks.length; i < len; i++) {
if (this._versionIds[i] !== this.editStacks[i].versionId) {
return false;
}
}
return true;
}
}
......@@ -428,29 +460,29 @@ export class UndoRedoService implements IUndoRedoService {
this._notificationService.error(err);
}
private _acquireLocks(affectedEditStacks: ResourceEditStack[]): () => void {
private _acquireLocks(editStackSnapshot: EditStackSnapshot): () => void {
// first, check if all locks can be acquired
for (const editStack of affectedEditStacks) {
for (const editStack of editStackSnapshot.editStacks) {
if (editStack.locked) {
throw new Error('Cannot acquire edit stack lock');
}
}
// can acquire all locks
for (const editStack of affectedEditStacks) {
for (const editStack of editStackSnapshot.editStacks) {
editStack.locked = true;
}
return () => {
// release all locks
for (const editStack of affectedEditStacks) {
for (const editStack of editStackSnapshot.editStacks) {
editStack.locked = false;
}
};
}
private _safeInvokeWithLocks(element: StackElement, invoke: () => Promise<void> | void, affectedEditStacks: ResourceEditStack[], cleanup: IDisposable = Disposable.None): Promise<void> | void {
const releaseLocks = this._acquireLocks(affectedEditStacks);
private _safeInvokeWithLocks(element: StackElement, invoke: () => Promise<void> | void, editStackSnapshot: EditStackSnapshot, cleanup: IDisposable = Disposable.None): Promise<void> | void {
const releaseLocks = this._acquireLocks(editStackSnapshot);
let result: Promise<void> | void;
try {
......@@ -492,15 +524,15 @@ export class UndoRedoService implements IUndoRedoService {
return result;
}
private _getAffectedEditStacks(element: WorkspaceStackElement): ResourceEditStack[] {
private _getAffectedEditStacks(element: WorkspaceStackElement): EditStackSnapshot {
const affectedEditStacks: ResourceEditStack[] = [];
for (const strResource of element.strResources) {
affectedEditStacks.push(this._editStacks.get(strResource)!);
}
return affectedEditStacks;
return new EditStackSnapshot(affectedEditStacks);
}
private _checkWorkspaceUndo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[], checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
private _checkWorkspaceUndo(resource: URI, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) {
this._splitPastWorkspaceElement(element, element.removedResources);
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
......@@ -516,7 +548,7 @@ export class UndoRedoService implements IUndoRedoService {
// this must be the last past element in all the impacted resources!
const cannotUndoDueToResources: URI[] = [];
for (const editStack of affectedEditStacks) {
for (const editStack of editStackSnapshot.editStacks) {
if (editStack.getClosestPastElement() !== element) {
cannotUndoDueToResources.push(editStack.resource);
}
......@@ -530,7 +562,7 @@ export class UndoRedoService implements IUndoRedoService {
}
const cannotLockDueToResources: URI[] = [];
for (const editStack of affectedEditStacks) {
for (const editStack of editStackSnapshot.editStacks) {
if (editStack.locked) {
cannotLockDueToResources.push(editStack.resource);
}
......@@ -543,6 +575,14 @@ export class UndoRedoService implements IUndoRedoService {
return new WorkspaceVerificationError(this.undo(resource));
}
// check if new stack elements were added in the meantime...
if (!editStackSnapshot.isValid()) {
this._splitPastWorkspaceElement(element, null);
const message = nls.localize('cannotWorkspaceUndoDueToInMeantimeUndoRedo', "Could not undo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label);
this._notificationService.info(message);
return new WorkspaceVerificationError(this.undo(resource));
}
return null;
}
......@@ -555,12 +595,13 @@ export class UndoRedoService implements IUndoRedoService {
return this._confirmAndExecuteWorkspaceUndo(resource, element, affectedEditStacks);
}
private async _confirmAndExecuteWorkspaceUndo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[]): Promise<void> {
private async _confirmAndExecuteWorkspaceUndo(resource: URI, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot): Promise<void> {
const result = await this._dialogService.show(
Severity.Info,
nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label),
[
nls.localize('ok', "Undo in {0} Files", affectedEditStacks.length),
nls.localize('ok', "Undo in {0} Files", editStackSnapshot.editStacks.length),
nls.localize('nok', "Undo this File"),
nls.localize('cancel', "Cancel"),
],
......@@ -583,7 +624,7 @@ export class UndoRedoService implements IUndoRedoService {
// choice: undo in all files
// At this point, it is possible that the element has been made invalid in the meantime (due to the confirmation await)
const verificationError1 = this._checkWorkspaceUndo(resource, element, affectedEditStacks, /*invalidated resources will be checked after the prepare call*/false);
const verificationError1 = this._checkWorkspaceUndo(resource, element, editStackSnapshot, /*invalidated resources will be checked after the prepare call*/false);
if (verificationError1) {
return verificationError1.returnValue;
}
......@@ -597,16 +638,16 @@ export class UndoRedoService implements IUndoRedoService {
}
// At this point, it is possible that the element has been made invalid in the meantime (due to the prepare await)
const verificationError2 = this._checkWorkspaceUndo(resource, element, affectedEditStacks, /*now also check that there are no more invalidated resources*/true);
const verificationError2 = this._checkWorkspaceUndo(resource, element, editStackSnapshot, /*now also check that there are no more invalidated resources*/true);
if (verificationError2) {
cleanup.dispose();
return verificationError2.returnValue;
}
for (const editStack of affectedEditStacks) {
for (const editStack of editStackSnapshot.editStacks) {
editStack.moveBackward(element);
}
return this._safeInvokeWithLocks(element, () => element.actual.undo(), affectedEditStacks, cleanup);
return this._safeInvokeWithLocks(element, () => element.actual.undo(), editStackSnapshot, cleanup);
}
private _resourceUndo(editStack: ResourceEditStack, element: ResourceStackElement): Promise<void> | void {
......@@ -621,7 +662,7 @@ export class UndoRedoService implements IUndoRedoService {
return;
}
editStack.moveBackward(element);
return this._safeInvokeWithLocks(element, () => element.actual.undo(), [editStack]);
return this._safeInvokeWithLocks(element, () => element.actual.undo(), new EditStackSnapshot([editStack]));
}
public undo(resource: URI): Promise<void> | void {
......@@ -652,7 +693,7 @@ export class UndoRedoService implements IUndoRedoService {
return false;
}
private _checkWorkspaceRedo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[], checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
private _checkWorkspaceRedo(resource: URI, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) {
this._splitFutureWorkspaceElement(element, element.removedResources);
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
......@@ -668,7 +709,7 @@ export class UndoRedoService implements IUndoRedoService {
// this must be the last future element in all the impacted resources!
const cannotRedoDueToResources: URI[] = [];
for (const editStack of affectedEditStacks) {
for (const editStack of editStackSnapshot.editStacks) {
if (editStack.getClosestFutureElement() !== element) {
cannotRedoDueToResources.push(editStack.resource);
}
......@@ -682,7 +723,7 @@ export class UndoRedoService implements IUndoRedoService {
}
const cannotLockDueToResources: URI[] = [];
for (const editStack of affectedEditStacks) {
for (const editStack of editStackSnapshot.editStacks) {
if (editStack.locked) {
cannotLockDueToResources.push(editStack.resource);
}
......@@ -695,6 +736,14 @@ export class UndoRedoService implements IUndoRedoService {
return new WorkspaceVerificationError(this.redo(resource));
}
// check if new stack elements were added in the meantime...
if (!editStackSnapshot.isValid()) {
this._splitPastWorkspaceElement(element, null);
const message = nls.localize('cannotWorkspaceRedoDueToInMeantimeUndoRedo', "Could not redo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label);
this._notificationService.info(message);
return new WorkspaceVerificationError(this.undo(resource));
}
return null;
}
......@@ -707,7 +756,7 @@ export class UndoRedoService implements IUndoRedoService {
return this._executeWorkspaceRedo(resource, element, affectedEditStacks);
}
private async _executeWorkspaceRedo(resource: URI, element: WorkspaceStackElement, affectedEditStacks: ResourceEditStack[]): Promise<void> {
private async _executeWorkspaceRedo(resource: URI, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot): Promise<void> {
// prepare
let cleanup: IDisposable;
try {
......@@ -717,16 +766,16 @@ export class UndoRedoService implements IUndoRedoService {
}
// At this point, it is possible that the element has been made invalid in the meantime (due to the prepare await)
const verificationError = this._checkWorkspaceRedo(resource, element, affectedEditStacks, /*now also check that there are no more invalidated resources*/true);
const verificationError = this._checkWorkspaceRedo(resource, element, editStackSnapshot, /*now also check that there are no more invalidated resources*/true);
if (verificationError) {
cleanup.dispose();
return verificationError.returnValue;
}
for (const editStack of affectedEditStacks) {
for (const editStack of editStackSnapshot.editStacks) {
editStack.moveForward(element);
}
return this._safeInvokeWithLocks(element, () => element.actual.redo(), affectedEditStacks, cleanup);
return this._safeInvokeWithLocks(element, () => element.actual.redo(), editStackSnapshot, cleanup);
}
private _resourceRedo(editStack: ResourceEditStack, element: ResourceStackElement): Promise<void> | void {
......@@ -741,7 +790,7 @@ export class UndoRedoService implements IUndoRedoService {
return;
}
editStack.moveForward(element);
return this._safeInvokeWithLocks(element, () => element.actual.redo(), [editStack]);
return this._safeInvokeWithLocks(element, () => element.actual.redo(), new EditStackSnapshot([editStack]));
}
public redo(resource: URI): Promise<void> | void {
......
......@@ -93,8 +93,8 @@ export class Win32UpdateService extends AbstractUpdateService {
protected buildUpdateFeedUrl(quality: string): string | undefined {
let platform = 'win32';
if (process.arch === 'x64') {
platform += '-x64';
if (process.arch !== 'ia32') {
platform += `-${process.arch}`;
}
if (getUpdateType() === UpdateType.Archive) {
......
......@@ -6,9 +6,11 @@
import { Delayer, disposableTimeout } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, toDisposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle';
import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService, ALL_SYNC_RESOURCES } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncLogService, IUserDataSyncService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService, ALL_SYNC_RESOURCES, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IProductService } from 'vs/platform/product/common/productService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
type AutoSyncClassification = {
sources: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
......@@ -34,22 +36,29 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IProductService private readonly productService: IProductService,
@IConfigurationService private readonly configurationService: IConfigurationService,
) {
super();
this.updateEnablement();
this.syncTriggerDelayer = this._register(new Delayer<void>(0));
this._register(Event.any(authTokenService.onDidChangeToken, userDataSyncService.onDidChangeStatus, this.userDataSyncEnablementService.onDidChangeEnablement)(() => this.updateEnablement()));
this._register(Event.filter(this.userDataSyncEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerAutoSync([RESOURCE_ENABLEMENT_SOURCE])));
if (getUserDataSyncStore(this.productService, this.configurationService)) {
this.updateAutoSync();
this._register(Event.any(authTokenService.onDidChangeToken, this.userDataSyncEnablementService.onDidChangeEnablement)(() => this.updateAutoSync()));
this._register(Event.filter(this.userDataSyncEnablementService.onDidChangeResourceEnablement, ([, enabled]) => enabled)(() => this.triggerAutoSync([RESOURCE_ENABLEMENT_SOURCE])));
}
}
private updateEnablement(): void {
private updateAutoSync(): void {
const { enabled, reason } = this.isAutoSyncEnabled();
if (enabled) {
if (this.autoSync.value === undefined) {
const autoSync = new AutoSync(this.startAutoSync(), 1000 * 60 * 5 /* 5 miutes */, this.userDataSyncService, this.logService);
autoSync.register(autoSync.onDidStartSync(() => this.lastSyncTriggerTime = new Date().getTime()));
autoSync.register(autoSync.onDidFinishSync(e => this.onDidFinishSync(e)));
this.autoSync.value = autoSync;
this.autoSync.value = new AutoSync(1000 * 60 * 5 /* 5 miutes */, this.userDataSyncService, this.logService);
this.autoSync.value.register(this.autoSync.value.onDidStartSync(() => this.lastSyncTriggerTime = new Date().getTime()));
this.autoSync.value.register(this.autoSync.value.onDidFinishSync(e => this.onDidFinishSync(e)));
if (this.startAutoSync()) {
this.autoSync.value.start();
}
}
} else {
if (this.autoSync.value !== undefined) {
......@@ -66,9 +75,6 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
if (!this.userDataSyncEnablementService.isEnabled()) {
return { enabled: false, reason: 'sync is disabled' };
}
if (this.userDataSyncService.status === SyncStatus.Uninitialized) {
return { enabled: false, reason: 'sync is not initialized' };
}
if (!this.authTokenService.token) {
return { enabled: false, reason: 'token is not avaialable' };
}
......@@ -145,21 +151,21 @@ class AutoSync extends Disposable {
readonly onDidFinishSync = this._onDidFinishSync.event;
constructor(
start: boolean,
private readonly interval: number /* in milliseconds */,
private readonly userDataSyncService: IUserDataSyncService,
private readonly logService: IUserDataSyncLogService,
) {
super();
if (start) {
this._register(this.onDidFinishSync(() => this.waitUntilNextIntervalAndSync()));
this._register(toDisposable(() => {
this.logService.info('Auto Sync: Stopped');
this.userDataSyncService.stop();
}));
this.logService.info('Auto Sync: Started');
this.sync(AutoSync.INTERVAL_SYNCING);
}
}
start(): void {
this._register(this.onDidFinishSync(() => this.waitUntilNextIntervalAndSync()));
this._register(toDisposable(() => {
this.userDataSyncService.stop();
this.logService.info('Auto Sync: Stopped');
}));
this.logService.info('Auto Sync: Started');
this.sync(AutoSync.INTERVAL_SYNCING);
}
private waitUntilNextIntervalAndSync(): void {
......
......@@ -9,6 +9,8 @@ import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron
import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IProductService } from 'vs/platform/product/common/productService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
......@@ -19,8 +21,10 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IAuthenticationTokenService authTokenService: IAuthenticationTokenService,
@ITelemetryService telemetryService: ITelemetryService,
@IProductService productService: IProductService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService);
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService, productService, configurationService);
this._register(Event.debounce<string, string[]>(Event.any<string>(
Event.map(electronService.onWindowFocus, () => 'windowFocus'),
......
......@@ -682,7 +682,7 @@ class CompositeBarModel {
private updateItemsOrder(): void {
if (this._items) {
this.items.forEach((item, index) => item.order = index);
this.items.forEach((item, index) => { if (item.order !== undefined) { item.order = index; } });
}
}
......
......@@ -94,8 +94,8 @@ class Extension implements IExtension {
return this.gallery.publisherDisplayName || this.gallery.publisher;
}
if (this.local!.metadata && this.local!.metadata.publisherDisplayName) {
return this.local!.metadata.publisherDisplayName;
if (this.local?.publisherDisplayName) {
return this.local.publisherDisplayName;
}
return this.local!.manifest.publisher;
......@@ -367,7 +367,7 @@ class Extensions extends Disposable {
}
// Sync the local extension with gallery extension if local extension doesnot has metadata
if (extension.local) {
const local = extension.local.metadata ? extension.local : await this.server.extensionManagementService.updateMetadata(extension.local, { id: compatible.identifier.uuid, publisherDisplayName: compatible.publisherDisplayName, publisherId: compatible.publisherId });
const local = extension.local.identifier.uuid ? extension.local : await this.server.extensionManagementService.updateMetadata(extension.local, { id: compatible.identifier.uuid, publisherDisplayName: compatible.publisherDisplayName, publisherId: compatible.publisherId });
extension.local = local;
extension.gallery = compatible;
this._onChange.fire({ extension });
......
......@@ -11,6 +11,8 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger';
import { IProductService } from 'vs/platform/product/common/productService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
......@@ -22,8 +24,10 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@IInstantiationService instantiationService: IInstantiationService,
@IHostService hostService: IHostService,
@ITelemetryService telemetryService: ITelemetryService,
@IProductService productService: IProductService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService);
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService, productService, configurationService);
this._register(Event.debounce<string, string[]>(Event.any<string>(
Event.map(hostService.onDidChangeFocus, () => 'windowFocus'),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册