diff --git a/src/main.ts b/src/main.ts index dfb8a240..5f0c0d91 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,6 @@ import program from 'commander'; import leven from 'leven'; -import { packageCommand, ls, Targets } from './package'; +import { packageCommand, ls, Targets, generateManifest } from './package'; import { publish, unpublish } from './publish'; import { show } from './show'; import { search } from './search'; @@ -197,6 +197,8 @@ module.exports = function (argv: string[]): void { ) .option('--no-update-package-json', 'Do not update `package.json`. Valid only when [version] is provided.') .option('-i, --packagePath ', 'Publish the provided VSIX packages.') + .option('--manifestPath ', 'Manifest files to publish alongside the VSIX packages.') + .option('--signaturePath ', 'Signature files to publish alongside the VSIX packages.') .option('--sigzipPath ', 'Signature archives to publish alongside the VSIX packages.') .option('--sign-tool ', 'Path to the VSIX signing tool. Will be invoked with two arguments: `SIGNTOOL `. This will be ignored if --sigzipPath is provided.') .option( @@ -237,6 +239,8 @@ module.exports = function (argv: string[]): void { gitTagVersion, updatePackageJson, packagePath, + manifestPath, + signaturePath, sigzipPath, githubBranch, gitlabBranch, @@ -269,6 +273,8 @@ module.exports = function (argv: string[]): void { gitTagVersion, updatePackageJson, packagePath, + manifestPath, + signaturePath, sigzipPath, githubBranch, gitlabBranch, @@ -298,6 +304,19 @@ module.exports = function (argv: string[]): void { .option('-f, --force', 'Skip confirmation prompt when unpublishing an extension') .action((id, { pat, azureCredential, force }) => main(unpublish({ id, pat, azureCredential, force }))); + program + .command('generate-manifest') + .description('Generates the extension manifest from the provided VSIX package.') + .requiredOption('-i, --packagePath ', 'Path to the VSIX package') + .option('-o, --out ', 'Output the extension manifest to location (defaults to .manifest)') + .action(( + packagePath, + out + ) => + main( + generateManifest(packagePath, out) + )); + program .command('ls-publishers') .description('Lists all known publishers') diff --git a/src/package.ts b/src/package.ts index 2d487252..867e9697 100644 --- a/src/package.ts +++ b/src/package.ts @@ -24,7 +24,7 @@ import { detectYarn, getDependencies } from './npm'; import * as GitHost from 'hosted-git-info'; import parseSemver from 'parse-semver'; import * as jsonc from 'jsonc-parser'; -import { generateManifest, zip } from '@vscode/vsce-sign'; +import * as vsceSign from '@vscode/vsce-sign'; const MinimatchOptions: minimatch.IOptions = { dot: true }; @@ -1850,14 +1850,27 @@ export async function signPackage(packageFile: string, signTool: string): Promis const signatureFile = path.join(packageFolder, `${packageName}.signature.p7s`); const signatureZip = path.join(packageFolder, `${packageName}.signature.zip`); - // Generate the signature manifest file await generateManifest(packageFile, manifestFile); // Sign the manifest file to generate the signature file cp.execSync(`${signTool} "${manifestFile}" "${signatureFile}"`, { stdio: 'inherit' }); - // Create a signature zip file containing the manifest and signature file - return zip(manifestFile, signatureFile, signatureZip); + return createSignatureArchive(manifestFile, signatureFile, signatureZip); +} + +// Generate the signature manifest file +export function generateManifest(packageFile: string, outputFile?: string): Promise { + if (!outputFile) { + const packageFolder = path.dirname(packageFile); + const packageName = path.basename(packageFile, '.vsix'); + outputFile = path.join(packageFolder, `${packageName}.manifest`); + } + return vsceSign.generateManifest(packageFile, outputFile); +} + +// Create a signature zip file containing the manifest and signature file +export async function createSignatureArchive(manifestFile: string, signatureFile: string, outputFile?: string): Promise { + return vsceSign.zip(manifestFile, signatureFile, outputFile) } export async function packageCommand(options: IPackageOptions = {}): Promise { diff --git a/src/publish.ts b/src/publish.ts index de7429dc..2d07b61d 100644 --- a/src/publish.ts +++ b/src/publish.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import { promisify } from 'util'; import * as semver from 'semver'; import { ExtensionQueryFlags, PublishedExtension } from 'azure-devops-node-api/interfaces/GalleryInterfaces'; -import { pack, readManifest, versionBump, prepublish, signPackage } from './package'; +import { pack, readManifest, versionBump, prepublish, signPackage, createSignatureArchive } from './package'; import * as tmp from 'tmp'; import { IVerifyPatOptions, getPublisher } from './store'; import { getGalleryAPI, read, getPublishedUrl, log, getHubUrl, patchOptionsWithManifest, getAzureCredentialAccessToken } from './util'; @@ -76,6 +76,8 @@ export interface IPublishOptions { readonly skipLicense?: boolean; readonly sigzipPath?: string[]; + readonly manifestPath?: string[]; + readonly signaturePath?: string[]; readonly signTool?: string; } @@ -89,6 +91,12 @@ export async function publish(options: IPublishOptions = {}): Promise { ); } + if (options.manifestPath || options.signaturePath) { + if (options.packagePath.length !== options.manifestPath?.length || options.packagePath.length !== options.signaturePath?.length) { + throw new Error(`Either all packages must be signed or none of them.`); + } + } + for (let index = 0; index < options.packagePath.length; index++) { const packagePath = options.packagePath[index]; const vsix = await readVSIXPackage(packagePath); @@ -118,12 +126,19 @@ export async function publish(options: IPublishOptions = {}): Promise { validateMarketplaceRequirements(vsix.manifest, options); - let sigzipPath = options.sigzipPath?.[index]; + let sigzipPath: string | undefined; + if (options.manifestPath?.[index] && options.signaturePath?.[index]) { + sigzipPath = await createSignatureArchive(options.manifestPath[index], options.signaturePath[index]); + } + + if (!sigzipPath) { + sigzipPath = options.sigzipPath?.[index]; + } + if (!sigzipPath && options.signTool) { sigzipPath = await signPackage(packagePath, options.signTool); } - await _publish(packagePath, sigzipPath, vsix.manifest, { ...options, target }); } } else {