mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
feat: add dmg for macOS (#289179)
* feat: add dmg for macOS arm64 * chore: create dmg for all archs * chore: move zx dependency to build * fix: invalid condition in universal build * chore: update background * fix: publishing universal dmg
This commit is contained in:
@@ -782,10 +782,16 @@ function getPlatform(product: string, os: string, arch: string, type: string): s
|
||||
case 'darwin':
|
||||
switch (product) {
|
||||
case 'client':
|
||||
if (arch === 'x64') {
|
||||
return 'darwin';
|
||||
switch (type) {
|
||||
case 'dmg':
|
||||
return `darwin-${arch}-dmg`;
|
||||
case 'archive':
|
||||
default:
|
||||
if (arch === 'x64') {
|
||||
return 'darwin';
|
||||
}
|
||||
return `darwin-${arch}`;
|
||||
}
|
||||
return `darwin-${arch}`;
|
||||
case 'server':
|
||||
if (arch === 'x64') {
|
||||
return 'server-darwin';
|
||||
|
||||
@@ -12,17 +12,25 @@ async function main() {
|
||||
const pipelineWorkspace = e('PIPELINE_WORKSPACE');
|
||||
|
||||
const folder = `${pipelineWorkspace}/vscode_client_darwin_${arch}_archive`;
|
||||
const dmgFolder = `${pipelineWorkspace}/vscode_client_darwin_${arch}_dmg`;
|
||||
const glob = `VSCode-darwin-${arch}.zip`;
|
||||
const dmgGlob = `VSCode-darwin-${arch}.dmg`;
|
||||
|
||||
// Codesign
|
||||
printBanner('Codesign');
|
||||
const codeSignTask = spawnCodesignProcess(esrpCliDLLPath, 'sign-darwin', folder, glob);
|
||||
await streamProcessOutputAndCheckResult('Codesign', codeSignTask);
|
||||
const archiveCodeSignTask = spawnCodesignProcess(esrpCliDLLPath, 'sign-darwin', folder, glob);
|
||||
const dmgCodeSignTask = spawnCodesignProcess(esrpCliDLLPath, 'sign-darwin', dmgFolder, dmgGlob);
|
||||
printBanner('Codesign Archive');
|
||||
await streamProcessOutputAndCheckResult('Codesign Archive', archiveCodeSignTask);
|
||||
printBanner('Codesign DMG');
|
||||
await streamProcessOutputAndCheckResult('Codesign DMG', dmgCodeSignTask);
|
||||
|
||||
// Notarize
|
||||
printBanner('Notarize');
|
||||
const notarizeTask = spawnCodesignProcess(esrpCliDLLPath, 'notarize-darwin', folder, glob);
|
||||
await streamProcessOutputAndCheckResult('Notarize', notarizeTask);
|
||||
const archiveNotarizeTask = spawnCodesignProcess(esrpCliDLLPath, 'notarize-darwin', folder, glob);
|
||||
const dmgNotarizeTask = spawnCodesignProcess(esrpCliDLLPath, 'notarize-darwin', dmgFolder, dmgGlob);
|
||||
printBanner('Notarize Archive');
|
||||
await streamProcessOutputAndCheckResult('Notarize Archive', archiveNotarizeTask);
|
||||
printBanner('Notarize DMG');
|
||||
await streamProcessOutputAndCheckResult('Notarize DMG', dmgNotarizeTask);
|
||||
}
|
||||
|
||||
main().then(() => {
|
||||
|
||||
@@ -14,6 +14,13 @@ jobs:
|
||||
sbomBuildDropPath: $(Build.ArtifactStagingDirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
sbomPackageName: "VS Code macOS $(VSCODE_ARCH)"
|
||||
sbomPackageVersion: $(Build.SourceVersion)
|
||||
- output: pipelineArtifact
|
||||
targetPath: $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_dmg/VSCode-darwin-$(VSCODE_ARCH).dmg
|
||||
artifactName: vscode_client_darwin_$(VSCODE_ARCH)_dmg
|
||||
displayName: Publish client DMG
|
||||
sbomBuildDropPath: $(Build.ArtifactStagingDirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
sbomPackageName: "VS Code macOS $(VSCODE_ARCH)"
|
||||
sbomPackageVersion: $(Build.SourceVersion)
|
||||
steps:
|
||||
- template: ../common/checkout.yml@self
|
||||
|
||||
@@ -112,8 +119,16 @@ jobs:
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
mkdir -p $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive
|
||||
pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip * && popd
|
||||
DMG_OUT="$(Pipeline.Workspace)/vscode_client_darwin_$(VSCODE_ARCH)_dmg"
|
||||
mkdir -p $DMG_OUT
|
||||
node build/darwin/create-dmg.ts $(agent.builddirectory) $DMG_OUT
|
||||
echo "##vso[task.setvariable variable=DMG_PATH]$DMG_OUT/VSCode-darwin-$(VSCODE_ARCH).dmg"
|
||||
displayName: Create DMG installer
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
mkdir -p $(Pipeline.Workspace)/vscode_client_darwin_$(VSCODE_ARCH)_archive
|
||||
pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(Pipeline.Workspace)/vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip * && popd
|
||||
displayName: Archive build
|
||||
|
||||
- task: UseDotNet@2
|
||||
@@ -132,17 +147,21 @@ jobs:
|
||||
Pattern: noop
|
||||
displayName: 'Install ESRP Tooling'
|
||||
|
||||
- script: node build/azure-pipelines/common/sign.ts $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll sign-darwin $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
displayName: ✍️ Codesign
|
||||
- pwsh: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$EsrpCodeSigningTool = (gci -directory -filter EsrpCodeSigning_* $(Agent.RootDirectory)/_tasks | Select-Object -last 1).FullName
|
||||
$Version = (gci -directory $EsrpCodeSigningTool | Select-Object -last 1).FullName
|
||||
echo "##vso[task.setvariable variable=EsrpCliDllPath]$Version/net6.0/esrpcli.dll"
|
||||
displayName: Find ESRP CLI
|
||||
|
||||
- script: node build/azure-pipelines/common/sign.ts $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll notarize-darwin $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip
|
||||
- script: node build/azure-pipelines/darwin/codesign.ts
|
||||
env:
|
||||
EsrpCliDllPath: $(EsrpCliDllPath)
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
displayName: ✍️ Notarize
|
||||
displayName: ✍️ Codesign & Notarize
|
||||
|
||||
- script: unzip $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip -d $(Build.ArtifactStagingDirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
- script: unzip $(Pipeline.Workspace)/vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip -d $(Build.ArtifactStagingDirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
displayName: Extract signed app
|
||||
|
||||
- script: |
|
||||
@@ -157,5 +176,8 @@ jobs:
|
||||
- script: |
|
||||
set -e
|
||||
mkdir -p $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_archive
|
||||
mv $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip
|
||||
mv $(Pipeline.Workspace)/vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip
|
||||
|
||||
mkdir -p $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_dmg
|
||||
mv $(DMG_PATH) $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_dmg/VSCode-darwin-$(VSCODE_ARCH).dmg
|
||||
displayName: Move artifact to out directory
|
||||
|
||||
@@ -69,6 +69,13 @@ jobs:
|
||||
sbomBuildDropPath: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web
|
||||
sbomPackageName: "VS Code macOS $(VSCODE_ARCH) Web"
|
||||
sbomPackageVersion: $(Build.SourceVersion)
|
||||
- output: pipelineArtifact
|
||||
targetPath: $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_dmg/VSCode-darwin-$(VSCODE_ARCH).dmg
|
||||
artifactName: vscode_client_darwin_$(VSCODE_ARCH)_dmg
|
||||
displayName: Publish client DMG
|
||||
sbomBuildDropPath: $(Build.ArtifactStagingDirectory)/VSCode-darwin-$(VSCODE_ARCH)
|
||||
sbomPackageName: "VS Code macOS $(VSCODE_ARCH)"
|
||||
sbomPackageVersion: $(Build.SourceVersion)
|
||||
steps:
|
||||
- template: ./steps/product-build-darwin-compile.yml@self
|
||||
parameters:
|
||||
|
||||
@@ -224,6 +224,15 @@ steps:
|
||||
DEBUG=electron-osx-sign* node build/darwin/sign.ts $(agent.builddirectory)
|
||||
displayName: Set Hardened Entitlements
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
DMG_OUT="$(Pipeline.Workspace)/vscode_client_darwin_$(VSCODE_ARCH)_dmg"
|
||||
mkdir -p $DMG_OUT
|
||||
node build/darwin/create-dmg.ts $(agent.builddirectory) $DMG_OUT
|
||||
echo "##vso[task.setvariable variable=DMG_PATH]$DMG_OUT/VSCode-darwin-$(VSCODE_ARCH).dmg"
|
||||
condition: eq(variables['BUILT_CLIENT'], 'true')
|
||||
displayName: Create DMG installer
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
ARCHIVE_PATH="$(Pipeline.Workspace)/vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip"
|
||||
@@ -298,6 +307,9 @@ steps:
|
||||
mv $(CLIENT_PATH) $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip
|
||||
fi
|
||||
|
||||
mkdir -p $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_dmg
|
||||
mv $(DMG_PATH) $(Build.ArtifactStagingDirectory)/out/vscode_client_darwin_$(VSCODE_ARCH)_dmg/VSCode-darwin-$(VSCODE_ARCH).dmg
|
||||
|
||||
mkdir -p $(Build.ArtifactStagingDirectory)/out/vscode_server_darwin_$(VSCODE_ARCH)_archive
|
||||
mv $(SERVER_PATH) $(Build.ArtifactStagingDirectory)/out/vscode_server_darwin_$(VSCODE_ARCH)_archive/vscode-server-darwin-$(VSCODE_ARCH).zip
|
||||
|
||||
|
||||
150
build/darwin/create-dmg.ts
Normal file
150
build/darwin/create-dmg.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { spawn } from '@malept/cross-spawn-promise';
|
||||
|
||||
const root = path.dirname(path.dirname(import.meta.dirname));
|
||||
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
|
||||
|
||||
interface DmgBuildSettings {
|
||||
title: string;
|
||||
icon?: string | null;
|
||||
background?: string;
|
||||
'background-color'?: string;
|
||||
'icon-size'?: number;
|
||||
'text-size'?: number;
|
||||
format?: string;
|
||||
window?: {
|
||||
position?: { x: number; y: number };
|
||||
size?: { width: number; height: number };
|
||||
};
|
||||
contents: Array<{
|
||||
path: string;
|
||||
x: number;
|
||||
y: number;
|
||||
type: 'file' | 'link';
|
||||
name?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
function getDmgBuilderPath(): string {
|
||||
return path.join(import.meta.dirname, '..', 'node_modules', 'dmg-builder');
|
||||
}
|
||||
|
||||
function getDmgBuilderVendorPath(): string {
|
||||
return path.join(getDmgBuilderPath(), 'vendor');
|
||||
}
|
||||
|
||||
async function runDmgBuild(settingsFile: string, volumeName: string, artifactPath: string): Promise<void> {
|
||||
const vendorDir = getDmgBuilderVendorPath();
|
||||
const scriptPath = path.join(vendorDir, 'run_dmgbuild.py');
|
||||
await spawn('python3', [scriptPath, '-s', settingsFile, volumeName, artifactPath], {
|
||||
cwd: vendorDir,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
}
|
||||
|
||||
async function main(buildDir?: string, outDir?: string): Promise<void> {
|
||||
const arch = process.env['VSCODE_ARCH'];
|
||||
const quality = process.env['VSCODE_QUALITY'];
|
||||
|
||||
if (!buildDir) {
|
||||
throw new Error('Build directory argument is required');
|
||||
}
|
||||
|
||||
if (!arch) {
|
||||
throw new Error('$VSCODE_ARCH not set');
|
||||
}
|
||||
|
||||
if (!outDir) {
|
||||
throw new Error('Output directory argument is required');
|
||||
}
|
||||
|
||||
const appRoot = path.join(buildDir, `VSCode-darwin-${arch}`);
|
||||
const appName = product.nameLong + '.app';
|
||||
const appPath = path.join(appRoot, appName);
|
||||
const dmgName = `VSCode-darwin-${arch}`;
|
||||
const artifactPath = path.join(outDir, `${dmgName}.dmg`);
|
||||
const backgroundPath = path.join(import.meta.dirname, `dmg-background-${quality}.tiff`);
|
||||
const appIconPath = path.join(appPath, 'Contents', 'Resources', `${product.nameShort}.icns`);
|
||||
let title = 'Code OSS';
|
||||
switch (quality) {
|
||||
case 'stable':
|
||||
title = 'VS Code';
|
||||
break;
|
||||
case 'insider':
|
||||
title = 'VS Code Insiders';
|
||||
break;
|
||||
case 'exploration':
|
||||
title = 'VS Code Exploration';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(appPath)) {
|
||||
throw new Error(`App path does not exist: ${appPath}`);
|
||||
}
|
||||
|
||||
console.log(`Creating DMG for ${product.nameLong}...`);
|
||||
console.log(` App path: ${appPath}`);
|
||||
console.log(` Output directory: ${outDir}`);
|
||||
console.log(` DMG name: ${dmgName}`);
|
||||
|
||||
if (fs.existsSync(artifactPath)) {
|
||||
fs.unlinkSync(artifactPath);
|
||||
}
|
||||
|
||||
const settings: DmgBuildSettings = {
|
||||
title,
|
||||
icon: appIconPath,
|
||||
background: backgroundPath,
|
||||
format: 'ULMO',
|
||||
'text-size': 12,
|
||||
window: {
|
||||
position: { x: 100, y: 400 },
|
||||
size: { width: 480, height: 320 }
|
||||
},
|
||||
contents: [
|
||||
{
|
||||
path: appPath,
|
||||
x: 120,
|
||||
y: 160,
|
||||
type: 'file'
|
||||
},
|
||||
{
|
||||
path: '/Applications',
|
||||
x: 360,
|
||||
y: 160,
|
||||
type: 'link'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const settingsFile = path.join(outDir, '.dmg-settings.json');
|
||||
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
|
||||
|
||||
try {
|
||||
await runDmgBuild(settingsFile, dmgName, artifactPath);
|
||||
} finally {
|
||||
if (fs.existsSync(settingsFile)) {
|
||||
fs.unlinkSync(settingsFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs.existsSync(artifactPath)) {
|
||||
throw new Error(`DMG was not created at expected path: ${artifactPath}`);
|
||||
}
|
||||
|
||||
const stats = fs.statSync(artifactPath);
|
||||
console.log(`Successfully created DMG: ${artifactPath} (${(stats.size / 1024 / 1024).toFixed(2)} MB)`);
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
main(process.argv[2], process.argv[3]).catch(err => {
|
||||
console.error('Failed to create DMG:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
BIN
build/darwin/dmg-background-insider.tiff
Normal file
BIN
build/darwin/dmg-background-insider.tiff
Normal file
Binary file not shown.
BIN
build/darwin/dmg-background-stable.tiff
Normal file
BIN
build/darwin/dmg-background-stable.tiff
Normal file
Binary file not shown.
@@ -44,6 +44,7 @@ export const unicodeFilter = Object.freeze<string[]>([
|
||||
'!**/*.test.ts',
|
||||
'!**/*.{d.ts,json,md}',
|
||||
'!**/*.mp3',
|
||||
'!**/*.tiff',
|
||||
|
||||
'!build/win32/**',
|
||||
'!extensions/markdown-language-features/notebook-out/*.js',
|
||||
@@ -138,6 +139,7 @@ export const indentationFilter = Object.freeze<string[]>([
|
||||
'!**/Dockerfile.*',
|
||||
'!**/*.Dockerfile',
|
||||
'!**/*.dockerfile',
|
||||
'!**/*.tiff',
|
||||
|
||||
// except for built files
|
||||
'!extensions/mermaid-chat-features/chat-webview-out/*.js',
|
||||
@@ -173,6 +175,7 @@ export const copyrightFilter = Object.freeze<string[]>([
|
||||
'!**/*.code-workspace',
|
||||
'!**/*.js.map',
|
||||
'!**/*.wasm',
|
||||
'!**/*.tiff',
|
||||
'!build/**/*.init',
|
||||
'!build/linux/libcxx-fetcher.*',
|
||||
'!build/npm/gyp/custom-headers/*.patch',
|
||||
|
||||
3134
build/package-lock.json
generated
3134
build/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -47,6 +47,7 @@
|
||||
"ansi-colors": "^3.2.3",
|
||||
"byline": "^5.0.0",
|
||||
"debug": "^4.3.2",
|
||||
"dmg-builder": "^26.5.0",
|
||||
"esbuild": "0.27.2",
|
||||
"extract-zip": "^2.0.1",
|
||||
"gulp-merge-json": "^2.1.1",
|
||||
@@ -60,7 +61,8 @@
|
||||
"tree-sitter": "^0.22.4",
|
||||
"vscode-universal-bundler": "^0.1.3",
|
||||
"workerpool": "^6.4.0",
|
||||
"yauzl": "^2.10.0"
|
||||
"yauzl": "^2.10.0",
|
||||
"zx": "^8.8.5"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -159,8 +159,7 @@
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-stream": "^7.0.0",
|
||||
"xml2js": "^0.5.0",
|
||||
"yaserver": "^0.4.0",
|
||||
"zx": "^8.8.5"
|
||||
"yaserver": "^0.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"windows-foreground-love": "0.6.1"
|
||||
@@ -18258,19 +18257,6 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zx": {
|
||||
"version": "8.8.5",
|
||||
"resolved": "https://registry.npmjs.org/zx/-/zx-8.8.5.tgz",
|
||||
"integrity": "sha512-SNgDF5L0gfN7FwVOdEFguY3orU5AkfFZm9B5YSHog/UDHv+lvmd82ZAsOenOkQixigwH2+yyH198AwNdKhj+RA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"zx": "build/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.17.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,8 +222,7 @@
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-stream": "^7.0.0",
|
||||
"xml2js": "^0.5.0",
|
||||
"yaserver": "^0.4.0",
|
||||
"zx": "^8.8.5"
|
||||
"yaserver": "^0.4.0"
|
||||
},
|
||||
"overrides": {
|
||||
"node-gyp-build": "4.8.1",
|
||||
|
||||
Reference in New Issue
Block a user