Skip to content

Commit

Permalink
No commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolaRHristov committed Aug 24, 2024
2 parents 949264c + 0ec0e7f commit 5a900ca
Show file tree
Hide file tree
Showing 9 changed files with 5,268 additions and 117 deletions.
16 changes: 13 additions & 3 deletions Source/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ export interface Contributions {

export type ExtensionKind = "ui" | "workspace" | "web";

export interface Manifest {
export interface ManifestPackage {
// mandatory (npm)
name: string;
version: string;
engines: { [name: string]: string };
engines: { vscode: string;[name: string]: string };

// vscode
publisher: string;
publisher?: string;
icon?: string;
contributes?: Contributions;
activationEvents?: string[];
Expand Down Expand Up @@ -125,3 +125,13 @@ export interface Manifest {
// preferGlobal
// publishConfig
}

export interface ManifestPublish extends ManifestPackage {
publisher: string;
}

type RecursivePartial<T> = {
[P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P];
};

export type UnverifiedManifest = RecursivePartial<ManifestPackage>;
7 changes: 2 additions & 5 deletions Source/nls.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Manifest } from "./manifest";
import { ManifestPackage } from './manifest';

export interface ITranslations {
[key: string]: string;
Expand Down Expand Up @@ -27,10 +27,7 @@ function createPatcher(translations: ITranslations): <T>(value: T) => T {
};
}

export function patchNLS(
manifest: Manifest,
translations: ITranslations,
): Manifest {
export function patchNLS(manifest: ManifestPackage, translations: ITranslations): ManifestPackage {
const patcher = createPatcher(translations);
return JSON.parse(
JSON.stringify(manifest, (_, value: any) => patcher(value)),
Expand Down
123 changes: 34 additions & 89 deletions Source/publish.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,19 @@
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,
createSignatureArchive,
} from "./package";
import * as tmp from "tmp";
import { IVerifyPatOptions, getPublisher } from "./store";
import {
getGalleryAPI,
read,
getPublishedUrl,
log,
getHubUrl,
patchOptionsWithManifest,
} from "./util";
import { Manifest } from "./manifest";
import { readVSIXPackage } from "./zip";
import { validatePublisher } from "./validation";
import { GalleryApi } from "azure-devops-node-api/GalleryApi";
import FormData from "form-data";
import { basename } from "path";
import { IterableBackoff, handleWhen, retry } from "cockatiel";
import { getAzureCredentialAccessToken } from "./auth";
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, createSignatureArchive } from './package';
import * as tmp from 'tmp';
import { IVerifyPatOptions, getPublisher } from './store';
import { getGalleryAPI, read, getPublishedUrl, log, getHubUrl, patchOptionsWithManifest } from './util';
import { ManifestPackage, ManifestPublish } from './manifest';
import { readVSIXPackage } from './zip';
import { validatePublisher } from './validation';
import { GalleryApi } from 'azure-devops-node-api/GalleryApi';
import FormData from 'form-data';
import { basename } from 'path';
import { IterableBackoff, handleWhen, retry } from 'cockatiel';
import { getAzureCredentialAccessToken } from './auth';

const tmpName = promisify(tmp.tmpName);

Expand Down Expand Up @@ -154,7 +137,7 @@ export async function publish(options: IPublishOptions = {}): Promise<any> {
}
}

validateMarketplaceRequirements(vsix.manifest, options);
const manifestValidated = validateManifestForPublishing(vsix.manifest, options);

let sigzipPath: string | undefined;
if (
Expand All @@ -175,52 +158,33 @@ export async function publish(options: IPublishOptions = {}): Promise<any> {
sigzipPath = await signPackage(packagePath, options.signTool);
}

await _publish(packagePath, sigzipPath, vsix.manifest, {
...options,
target,
});
await _publish(packagePath, sigzipPath, manifestValidated, { ...options, target });
}
} else {
const cwd = options.cwd || process.cwd();
const manifest = await readManifest(cwd);
patchOptionsWithManifest(options, manifest);

// Validate marketplace requirements before prepublish to avoid unnecessary work
validateMarketplaceRequirements(manifest, options);
validateManifestForPublishing(manifest, options);

await prepublish(cwd, manifest, options.useYarn);
await versionBump(options);

if (options.targets) {
for (const target of options.targets) {
const packagePath = await tmpName();
const packageResult = await pack({
...options,
target,
packagePath,
});
const sigzipPath = options.signTool
? await signPackage(packagePath, options.signTool)
: undefined;
await _publish(
packagePath,
sigzipPath,
packageResult.manifest,
{ ...options, target },
);
const packageResult = await pack({ ...options, target, packagePath });
const manifestValidated = validateManifestForPublishing(packageResult.manifest, options);
const sigzipPath = options.signTool ? await signPackage(packagePath, options.signTool) : undefined;
await _publish(packagePath, sigzipPath, manifestValidated, { ...options, target });
}
} else {
const packagePath = await tmpName();
const packageResult = await pack({ ...options, packagePath });
const sigzipPath = options.signTool
? await signPackage(packagePath, options.signTool)
: undefined;
await _publish(
packagePath,
sigzipPath,
packageResult.manifest,
options,
);
const manifestValidated = validateManifestForPublishing(packageResult.manifest, options);
const sigzipPath = options.signTool ? await signPackage(packagePath, options.signTool) : undefined;
await _publish(packagePath, sigzipPath, manifestValidated, options);
}
}
}
Expand All @@ -235,12 +199,7 @@ export interface IInternalPublishOptions {
readonly skipDuplicate?: boolean;
}

async function _publish(
packagePath: string,
sigzipPath: string | undefined,
manifest: Manifest,
options: IInternalPublishOptions,
) {
async function _publish(packagePath: string, sigzipPath: string | undefined, manifest: ManifestPublish, options: IInternalPublishOptions) {
const pat = await getPAT(manifest.publisher, options);
const api = await getGalleryAPI(pat);
const packageStream = fs.createReadStream(packagePath);
Expand Down Expand Up @@ -351,15 +310,8 @@ async function _publish(
log.done(`Published ${description}.`);
}

async function _publishSignedPackage(
api: GalleryApi,
packageName: string,
packageStream: fs.ReadStream,
sigzipName: string,
sigzipStream: fs.ReadStream,
manifest: Manifest,
) {
const extensionType = "Visual Studio Code";
async function _publishSignedPackage(api: GalleryApi, packageName: string, packageStream: fs.ReadStream, sigzipName: string, sigzipStream: fs.ReadStream, manifest: ManifestPublish) {
const extensionType = 'Visual Studio Code';
const form = new FormData();
const lineBreak = "\r\n";
form.setBoundary("0f411892-ef48-488f-89d3-4f0546e84723");
Expand Down Expand Up @@ -401,7 +353,7 @@ export async function unpublish(options: IUnpublishOptions = {}): Promise<any> {
[publisher, name] = options.id.split(".");
} else {
const manifest = await readManifest(options.cwd);
publisher = manifest.publisher;
publisher = validatePublisher(manifest.publisher);
name = manifest.name;
}

Expand All @@ -424,17 +376,8 @@ export async function unpublish(options: IUnpublishOptions = {}): Promise<any> {
log.done(`Deleted extension: ${fullName}!`);
}

function validateMarketplaceRequirements(
manifest: Manifest,
options: IInternalPublishOptions,
) {
validatePublisher(manifest.publisher);

if (
manifest.enableProposedApi &&
!options.allowAllProposedApis &&
!options.noVerify
) {
function validateManifestForPublishing(manifest: ManifestPackage, options: IInternalPublishOptions): ManifestPublish {
if (manifest.enableProposedApi && !options.allowAllProposedApis && !options.noVerify) {
throw new Error(
"Extensions using proposed API (enableProposedApi: true) can't be published to the Marketplace. Use --allow-all-proposed-apis to bypass. https://code.visualstudio.com/api/advanced-topics/using-proposed-api",
);
Expand All @@ -458,6 +401,8 @@ function validateMarketplaceRequirements(
`The VS Marketplace doesn't support prerelease versions: '${manifest.version}'. Checkout our pre-release versioning recommendation here: https://code.visualstudio.com/api/working-with-extensions/publishing-extension#prerelease-extensions`,
);
}

return { ...manifest, publisher: validatePublisher(manifest.publisher) };
}

export async function getPAT(
Expand Down
3 changes: 1 addition & 2 deletions Source/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,7 @@ export interface IVerifyPatOptions {
}

export async function verifyPat(options: IVerifyPatOptions): Promise<void> {
const publisherName =
options.publisherName ?? (await readManifest()).publisher;
const publisherName = options.publisherName ?? validatePublisher((await readManifest()).publisher);
const pat = await getPAT(publisherName, options);

try {
Expand Down
25 changes: 11 additions & 14 deletions Source/util.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { promisify } from "util";
import * as fs from "fs";
import _read from "read";
import { WebApi, getBasicHandler } from "azure-devops-node-api/WebApi";
import { IGalleryApi, GalleryApi } from "azure-devops-node-api/GalleryApi";
import chalk from "chalk";
import { PublicGalleryAPI } from "./publicgalleryapi";
import { ISecurityRolesApi } from "azure-devops-node-api/SecurityRolesApi";
import { Manifest } from "./manifest";
import { EOL } from "os";
import { promisify } from 'util';
import * as fs from 'fs';
import _read from 'read';
import { WebApi, getBasicHandler } from 'azure-devops-node-api/WebApi';
import { IGalleryApi, GalleryApi } from 'azure-devops-node-api/GalleryApi';
import chalk from 'chalk';
import { PublicGalleryAPI } from './publicgalleryapi';
import { ISecurityRolesApi } from 'azure-devops-node-api/SecurityRolesApi';
import { ManifestPackage } from './manifest';
import { EOL } from 'os';

const __read = promisify<_read.Options, string>(_read);
export function read(
Expand Down Expand Up @@ -201,10 +201,7 @@ export const log = {
error: _log.bind(null, LogMessageType.ERROR) as LogFn,
};

export function patchOptionsWithManifest(
options: any,
manifest: Manifest,
): void {
export function patchOptionsWithManifest(options: any, manifest: ManifestPackage): void {
if (!manifest.vsce) {
return;
}
Expand Down
16 changes: 12 additions & 4 deletions Source/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import parseSemver from "parse-semver";

const nameRegex = /^[a-z0-9][a-z0-9\-]*$/i;

export function validatePublisher(publisher: string): void {
export function validatePublisher(publisher: string | undefined): string {
if (!publisher) {
throw new Error(
`Missing publisher name. Learn more: https://code.visualstudio.com/api/working-with-extensions/publishing-extension#publishing-extensions`,
Expand All @@ -15,29 +15,35 @@ export function validatePublisher(publisher: string): void {
`Invalid publisher name '${publisher}'. Expected the identifier of a publisher, not its human-friendly name. Learn more: https://code.visualstudio.com/api/working-with-extensions/publishing-extension#publishing-extensions`,
);
}

return publisher;
}

export function validateExtensionName(name: string): void {
export function validateExtensionName(name: string | undefined): string {
if (!name) {
throw new Error(`Missing extension name`);
}

if (!nameRegex.test(name)) {
throw new Error(`Invalid extension name '${name}'`);
}

return name;
}

export function validateVersion(version: string): void {
export function validateVersion(version: string | undefined): string {
if (!version) {
throw new Error(`Missing extension version`);
}

if (!semver.valid(version)) {
throw new Error(`Invalid extension version '${version}'`);
}

return version;
}

export function validateEngineCompatibility(version: string): void {
export function validateEngineCompatibility(version: string | undefined): string {
if (!version) {
throw new Error(`Missing vscode engine compatibility version`);
}
Expand All @@ -49,6 +55,8 @@ export function validateEngineCompatibility(version: string): void {
`Invalid vscode engine compatibility version '${version}'`,
);
}

return version;
}

/**
Expand Down
Loading

0 comments on commit 5a900ca

Please sign in to comment.