Skip to content

Commit f5e06c1

Browse files
authored
feat: support device permissions via package identity (microsoft#257741)
* feat: support device permissions via package identity * chore: update explorer dll checksums * chore: cleanup appx preparation * chore: avoid cross-device link error * chore: remove appx installation gate * chore: rm duplicate appx installation checks * chore: extract package full name from Get-AppxPackage * chore: fix remove-appxpackage command
1 parent 1bd1003 commit f5e06c1

File tree

8 files changed

+196
-61
lines changed

8 files changed

+196
-61
lines changed

build/azure-pipelines/win32/product-build-win32.yml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,9 @@ steps:
175175
displayName: Transpile client and extensions
176176

177177
- ${{ else }}:
178-
- ${{ if and(ne(parameters.VSCODE_CIBUILD, true), eq(parameters.VSCODE_QUALITY, 'insider')) }}:
179-
- powershell: node build/win32/explorer-appx-fetcher .build/win32/appx
180-
displayName: Download Explorer Sparse Package
178+
- ${{ if and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'exploration')) }}:
179+
- powershell: node build/win32/explorer-dll-fetcher .build/win32/appx
180+
displayName: Download Explorer dll
181181

182182
- powershell: |
183183
. build/azure-pipelines/win32/exec.ps1
@@ -190,6 +190,23 @@ steps:
190190
GITHUB_TOKEN: "$(github-distro-mixin-password)"
191191
displayName: Build client
192192
193+
# Note: the appx prepare step has to follow Build client step since build step replaces the template
194+
# strings in the raw manifest file at resources/win32/appx/AppxManifest.xml and places it under
195+
# <build-out-dir>/appx/manifest, we need a separate step to prepare the appx package with the
196+
# final contents. In our case only the manifest file is bundled into the appx package.
197+
- ${{ if and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'exploration')) }}:
198+
- powershell: |
199+
. build/azure-pipelines/win32/exec.ps1
200+
$ErrorActionPreference = "Stop"
201+
# Add Windows SDK to path
202+
$sdk = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64"
203+
$env:PATH = "$sdk;$env:PATH"
204+
$AppxName = if ('$(VSCODE_QUALITY)' -eq 'stable') { 'code' } else { 'code_insider' }
205+
makeappx pack /d "$(Agent.BuildDirectory)/VSCode-win32-$(VSCODE_ARCH)/appx/manifest" /p "$(Agent.BuildDirectory)/VSCode-win32-$(VSCODE_ARCH)/appx/${AppxName}_$(VSCODE_ARCH).appx" /nv
206+
# Remove the raw manifest folder
207+
Remove-Item -Path "$(Agent.BuildDirectory)/VSCode-win32-$(VSCODE_ARCH)/appx/manifest" -Recurse -Force
208+
displayName: Prepare appx package
209+
193210
- powershell: |
194211
. build/azure-pipelines/win32/exec.ps1
195212
$ErrorActionPreference = "Stop"

build/checksums/explorer-dll.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
11b36db4f244693381e52316261ce61678286f6bdfe2614c6352f6fecf3f060d code_explorer_command_arm64.dll
2+
bfab3719038ca46bcd8afb9249a00f851dd08aa3cc8d13d01a917111a2a6d7c2 code_explorer_command_x64.dll
3+
b5cd79c1e91390bdeefaf35cc5c62a6022220832e145781e5609913fac706ad9 code_insider_explorer_command_arm64.dll
4+
f04335cc6fbe8425bd5516e6acbfa05ca706fd7566799a1e22fca1344c25351f code_insider_explorer_command_x64.dll

build/gulpfile.vscode.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,8 +404,21 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
404404
result = es.merge(result, gulp.src('.build/policies/win32/**', { base: '.build/policies/win32' })
405405
.pipe(rename(f => f.dirname = `policies/${f.dirname}`)));
406406

407-
if (quality === 'insider') {
407+
if (quality !== 'exploration') {
408408
result = es.merge(result, gulp.src('.build/win32/appx/**', { base: '.build/win32' }));
409+
const rawVersion = version.replace(/-\w+$/, '').split('.');
410+
const appxVersion = `${rawVersion[0]}.0.${rawVersion[1]}.${rawVersion[2]}`;
411+
result = es.merge(result, gulp.src('resources/win32/appx/AppxManifest.xml', { base: '.' })
412+
.pipe(replace('@@AppxPackageName@@', product.win32AppUserModelId))
413+
.pipe(replace('@@AppxPackageVersion@@', appxVersion))
414+
.pipe(replace('@@AppxPackageDisplayName@@', product.nameLong))
415+
.pipe(replace('@@AppxPackageDescription@@', product.win32NameVersion))
416+
.pipe(replace('@@ApplicationIdShort@@', product.win32RegValueName))
417+
.pipe(replace('@@ApplicationExe@@', product.nameShort + '.exe'))
418+
.pipe(replace('@@FileExplorerContextMenuID@@', quality === 'stable' ? 'OpenWithCode' : 'OpenWithCodeInsiders'))
419+
.pipe(replace('@@FileExplorerContextMenuCLSID@@', product.win32ContextMenu[arch].clsid))
420+
.pipe(replace('@@FileExplorerContextMenuDLL@@', `${quality === 'stable' ? 'code' : 'code_insider'}_explorer_command_${arch}.dll`))
421+
.pipe(rename(f => f.dirname = `appx/manifest`)));
409422
}
410423
} else if (platform === 'linux') {
411424
result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' })

build/gulpfile.vscode.win32.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,9 @@ function buildWin32Setup(arch, target) {
112112
Quality: quality
113113
};
114114

115-
if (quality === 'insider') {
116-
definitions['AppxPackage'] = `code_insiders_explorer_${arch}.appx`;
117-
definitions['AppxPackageFullname'] = `Microsoft.${product.win32RegValueName}_1.0.0.0_neutral__8wekyb3d8bbwe`;
118-
definitions['AppxPackageName'] = `Microsoft.${product.win32RegValueName}`;
115+
if (quality !== 'exploration') {
116+
definitions['AppxPackage'] = `${quality === 'stable' ? 'code' : 'code_insider'}_${arch}.appx`;
117+
definitions['AppxPackageName'] = `${product.win32AppUserModelId}`;
119118
}
120119

121120
packageInnoSetup(issPath, { definitions }, cb);

build/win32/code.iss

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ Name: "{app}"; AfterInstall: DisableAppDirInheritance
9494
Source: "*"; Excludes: "\CodeSignSummary*.md,\tools,\tools\*,\appx,\appx\*,\resources\app\product.json"; DestDir: "{code:GetDestDir}"; Flags: ignoreversion recursesubdirs createallsubdirs
9595
Source: "tools\*"; DestDir: "{app}\tools"; Flags: ignoreversion
9696
Source: "{#ProductJsonPath}"; DestDir: "{code:GetDestDir}\resources\app"; Flags: ignoreversion
97-
#ifdef AppxPackageFullname
98-
Source: "appx\*"; DestDir: "{app}\appx"; BeforeInstall: RemoveAppxPackage; AfterInstall: AddAppxPackage; Flags: ignoreversion; Check: IsWindows11OrLater and QualityIsInsiders
97+
#ifdef AppxPackageName
98+
Source: "appx\*"; DestDir: "{app}\appx"; BeforeInstall: RemoveAppxPackage; AfterInstall: AddAppxPackage; Flags: ignoreversion; Check: IsWindows11OrLater
9999
#endif
100100

101101
[Icons]
@@ -1466,26 +1466,26 @@ begin
14661466
Result := False;
14671467
end;
14681468
1469-
#ifdef AppxPackageFullname
1469+
#ifdef AppxPackageName
14701470
var
1471-
Line: String;
1471+
AppxPackageFullname: String;
14721472
14731473
procedure ExecAndGetFirstLineLog(const S: String; const Error, FirstLine: Boolean);
14741474
begin
1475-
if not Error and (Line = '') and (Trim(S) <> '') then
1476-
Line := S;
1475+
if not Error and (AppxPackageFullname = '') and (Trim(S) <> '') then
1476+
AppxPackageFullname := S;
14771477
Log(S);
14781478
end;
14791479
14801480
function AppxPackageInstalled(var ResultCode: Integer): Boolean;
14811481
begin
1482-
Line := '';
1482+
AppxPackageFullname := '';
14831483
try
1484-
ExecAndLogOutput('powershell.exe', '-Command ' + AddQuotes('Get-AppxPackage -Name ''{#AppxPackageName}'''), '', SW_HIDE, ewWaitUntilTerminated, ResultCode, @ExecAndGetFirstLineLog);
1484+
ExecAndLogOutput('powershell.exe', '-Command ' + AddQuotes('Get-AppxPackage -Name ''{#AppxPackageName}'' | Select-Object -ExpandProperty PackageFullName'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode, @ExecAndGetFirstLineLog);
14851485
except
14861486
Log(GetExceptionMessage);
14871487
end;
1488-
if (Line <> '') then
1488+
if (AppxPackageFullname <> '') then
14891489
Result := True
14901490
else
14911491
Result := False
@@ -1495,7 +1495,7 @@ procedure AddAppxPackage();
14951495
var
14961496
AddAppxPackageResultCode: Integer;
14971497
begin
1498-
if not AppxPackageInstalled(AddAppxPackageResultCode) and WizardIsTaskSelected('addcontextmenufiles') then begin
1498+
if not AppxPackageInstalled(AddAppxPackageResultCode) then begin
14991499
ShellExec('', 'powershell.exe', '-Command ' + AddQuotes('Add-AppxPackage -Path ''' + ExpandConstant('{app}\appx\{#AppxPackage}') + ''' -ExternalLocation ''' + ExpandConstant('{app}\appx') + ''''), '', SW_HIDE, ewWaitUntilTerminated, AddAppxPackageResultCode);
15001500
end;
15011501
end;
@@ -1505,7 +1505,7 @@ var
15051505
RemoveAppxPackageResultCode: Integer;
15061506
begin
15071507
if AppxPackageInstalled(RemoveAppxPackageResultCode) then begin
1508-
ShellExec('', 'powershell.exe', '-Command ' + AddQuotes('Remove-AppxPackage -Package ''{#AppxPackageFullname}'''), '', SW_HIDE, ewWaitUntilTerminated, RemoveAppxPackageResultCode);
1508+
ShellExec('', 'powershell.exe', '-Command ' + AddQuotes('Remove-AppxPackage -Package ''' + AppxPackageFullname + ''''), '', SW_HIDE, ewWaitUntilTerminated, RemoveAppxPackageResultCode);
15091509
end;
15101510
end;
15111511
#endif
@@ -1517,7 +1517,7 @@ var
15171517
begin
15181518
if CurStep = ssPostInstall then
15191519
begin
1520-
#ifdef AppxPackageFullname
1520+
#ifdef AppxPackageName
15211521
if not WizardIsTaskSelected('addcontextmenufiles') then begin
15221522
RegDeleteKeyIncludingSubkeys({#EnvironmentRootKey}, 'Software\Classes\{#RegValueName}ContextMenu');
15231523
end else begin
@@ -1606,15 +1606,12 @@ var
16061606
Parts: TArrayOfString;
16071607
NewPath: string;
16081608
i: Integer;
1609-
ResultCode: Integer;
16101609
begin
16111610
if not CurUninstallStep = usUninstall then begin
16121611
exit;
16131612
end;
1614-
#ifdef AppxPackageFullname
1615-
if AppxPackageInstalled(ResultCode) then begin
1616-
RemoveAppxPackage();
1617-
end;
1613+
#ifdef AppxPackageName
1614+
RemoveAppxPackage();
16181615
#endif
16191616
if not RegQueryStringValue({#EnvironmentRootKey}, '{#EnvironmentKey}', 'Path', Path)
16201617
then begin

build/win32/explorer-appx-fetcher.js renamed to build/win32/explorer-dll-fetcher.js

Lines changed: 26 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/win32/explorer-appx-fetcher.ts renamed to build/win32/explorer-dll-fetcher.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,50 @@
77

88
import fs from 'fs';
99
import debug from 'debug';
10-
import extract from 'extract-zip';
1110
import path from 'path';
1211
import { downloadArtifact } from '@electron/get';
12+
import product from '../../product.json';
1313

14-
const root = path.dirname(path.dirname(__dirname));
14+
const d = debug('explorer-dll-fetcher');
1515

16-
const d = debug('explorer-appx-fetcher');
17-
18-
export async function downloadExplorerAppx(outDir: string, quality: string = 'stable', targetArch: string = 'x64'): Promise<void> {
19-
const fileNamePrefix = quality === 'insider' ? 'code_insiders' : 'code';
20-
const fileName = `${fileNamePrefix}_explorer_${targetArch}.zip`;
21-
22-
if (await fs.existsSync(path.resolve(outDir, 'resources.pri'))) {
23-
return;
24-
}
16+
export async function downloadExplorerDll(outDir: string, quality: string = 'stable', targetArch: string = 'x64'): Promise<void> {
17+
const fileNamePrefix = quality === 'insider' ? 'code_insider' : 'code';
18+
const fileName = `${fileNamePrefix}_explorer_command_${targetArch}.dll`;
2519

2620
if (!await fs.existsSync(outDir)) {
2721
await fs.mkdirSync(outDir, { recursive: true });
2822
}
2923

24+
// Read and parse checksums file
25+
const checksumsFilePath = path.join(path.dirname(__dirname), 'checksums', 'explorer-dll.txt');
26+
const checksumsContent = fs.readFileSync(checksumsFilePath, 'utf8');
27+
const checksums: Record<string, string> = {};
28+
29+
checksumsContent.split('\n').forEach(line => {
30+
const trimmedLine = line.trim();
31+
if (trimmedLine) {
32+
const [checksum, filename] = trimmedLine.split(/\s+/);
33+
if (checksum && filename) {
34+
checksums[filename] = checksum;
35+
}
36+
}
37+
});
38+
3039
d(`downloading ${fileName}`);
3140
const artifact = await downloadArtifact({
3241
isGeneric: true,
33-
version: '3.0.4',
42+
version: 'v4.0.0-350164',
3443
artifactName: fileName,
35-
unsafelyDisableChecksums: true,
44+
checksums,
3645
mirrorOptions: {
3746
mirror: 'https://github.com/microsoft/vscode-explorer-command/releases/download/',
38-
customDir: '3.0.4',
47+
customDir: 'v4.0.0-350164',
3948
customFilename: fileName
4049
}
4150
});
4251

43-
d(`unpacking from ${fileName}`);
44-
await extract(artifact, { dir: fs.realpathSync(outDir) });
52+
d(`moving ${artifact} to ${outDir}`);
53+
await fs.copyFileSync(artifact, path.join(outDir, fileName));
4554
}
4655

4756
async function main(outputDir?: string): Promise<void> {
@@ -51,8 +60,7 @@ async function main(outputDir?: string): Promise<void> {
5160
throw new Error('Required build env not set');
5261
}
5362

54-
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
55-
await downloadExplorerAppx(outputDir, (product as any).quality, arch);
63+
await downloadExplorerDll(outputDir, (product as any).quality, arch);
5664
}
5765

5866
if (require.main === module) {

0 commit comments

Comments
 (0)