parent
165f96d2dd
commit
3502fba665
9 changed files with 65 additions and 88 deletions
|
@ -34,6 +34,7 @@
|
|||
"node": ">= 8"
|
||||
},
|
||||
"dependencies": {
|
||||
"azure-devops-node-api": "^7.2.0",
|
||||
"chalk": "^2.4.2",
|
||||
"cheerio": "^1.0.0-rc.1",
|
||||
"commander": "^2.8.1",
|
||||
|
@ -49,8 +50,8 @@
|
|||
"read": "^1.0.7",
|
||||
"semver": "^5.1.0",
|
||||
"tmp": "0.0.29",
|
||||
"typed-rest-client": "1.2.0",
|
||||
"url-join": "^1.1.0",
|
||||
"vso-node-api": "6.1.2-preview",
|
||||
"yauzl": "^2.3.1",
|
||||
"yazl": "^2.2.2"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as program from 'commander';
|
||||
import { packageCommand, ls } from './package';
|
||||
import { publish, list, unpublish } from './publish';
|
||||
import { publish, unpublish } from './publish';
|
||||
import { show } from './show';
|
||||
import { search } from './search';
|
||||
import { listPublishers, createPublisher, deletePublisher, loginPublisher, logoutPublisher } from './store';
|
||||
|
@ -88,11 +88,6 @@ module.exports = function (argv: string[]): void {
|
|||
.option('-p, --pat <token>', 'Personal Access Token')
|
||||
.action((id, { pat }) => main(unpublish({ id, pat })));
|
||||
|
||||
program
|
||||
.command('list <publisher>')
|
||||
.description('Lists all extensions published by the given publisher')
|
||||
.action(publisher => main(list(publisher)));
|
||||
|
||||
program
|
||||
.command('ls-publishers')
|
||||
.description('List all known publishers')
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { HttpClient, HttpClientResponse } from 'vso-node-api/HttpClient';
|
||||
import { PublishedExtension, ExtensionQueryFlags, FilterCriteria, SortOrderType,
|
||||
SortByType, ExtensionQueryFilterType, TypeInfo} from 'vso-node-api/interfaces/GalleryInterfaces';
|
||||
import { IHeaders } from 'vso-node-api/interfaces/common/VsoBaseInterfaces';
|
||||
import { ContractSerializer } from 'vso-node-api/Serialization';
|
||||
import { HttpClient, HttpClientResponse } from 'typed-rest-client/HttpClient';
|
||||
import {
|
||||
PublishedExtension, ExtensionQueryFlags, FilterCriteria, SortOrderType,
|
||||
SortByType, ExtensionQueryFilterType, TypeInfo
|
||||
} from 'azure-devops-node-api/interfaces/GalleryInterfaces';
|
||||
import { IHeaders } from 'azure-devops-node-api/interfaces/common/VsoBaseInterfaces';
|
||||
import { ContractSerializer } from 'azure-devops-node-api/Serialization';
|
||||
|
||||
export interface ExtensionQuery {
|
||||
pageNumber?: number;
|
||||
|
@ -21,7 +23,7 @@ export class PublicGalleryAPI {
|
|||
this.client = new HttpClient('vsce');
|
||||
}
|
||||
|
||||
post(url: string, data: string, additionalHeaders?: IHeaders): Promise<HttpClientResponse> {
|
||||
private post(url: string, data: string, additionalHeaders?: IHeaders): Promise<HttpClientResponse> {
|
||||
return this.client.post(`${this.baseUrl}/_apis/public${url}`, data, additionalHeaders);
|
||||
}
|
||||
|
||||
|
@ -35,17 +37,17 @@ export class PublicGalleryAPI {
|
|||
assetTypes = [],
|
||||
}: ExtensionQuery): Promise<PublishedExtension[]> {
|
||||
return this.post('/gallery/extensionquery', JSON.stringify({
|
||||
filters: [{pageNumber, pageSize, criteria}],
|
||||
filters: [{ pageNumber, pageSize, criteria }],
|
||||
assetTypes,
|
||||
flags: flags.reduce((memo, flag) => memo | flag, 0)
|
||||
}), {
|
||||
Accept: `application/json;api-version=${this.apiVersion}`,
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
Accept: `application/json;api-version=${this.apiVersion}`,
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
.then(res => res.readBody())
|
||||
.then(data => JSON.parse(data))
|
||||
.then(({results: [result = {}] = []}) => result)
|
||||
.then(({extensions = []}) =>
|
||||
.then(({ results: [result = {}] = [] }) => result)
|
||||
.then(({ extensions = [] }) =>
|
||||
ContractSerializer.deserialize(extensions, TypeInfo.PublishedExtension, false, false)
|
||||
);
|
||||
}
|
||||
|
@ -55,9 +57,9 @@ export class PublicGalleryAPI {
|
|||
criteria: [{ filterType: ExtensionQueryFilterType.Name, value: extensionId }],
|
||||
flags,
|
||||
})
|
||||
.then(result => result.filter(({publisher: {publisherName}, extensionName}) =>
|
||||
extensionId.toLowerCase() === `${publisherName}.${extensionName}`.toLowerCase())
|
||||
)
|
||||
.then(([extension]) => extension);
|
||||
.then(result => result.filter(({ publisher: { publisherName }, extensionName }) =>
|
||||
extensionId.toLowerCase() === `${publisherName}.${extensionName}`.toLowerCase())
|
||||
)
|
||||
.then(([extension]) => extension);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as fs from 'fs';
|
||||
import { ExtensionQueryFlags, PublishedExtension, ExtensionQueryFilterType, PagingDirection, SortByType, SortOrderType } from 'vso-node-api/interfaces/GalleryInterfaces';
|
||||
import { ExtensionQueryFlags, PublishedExtension, ExtensionQueryFilterType, PagingDirection, SortByType, SortOrderType } from 'azure-devops-node-api/interfaces/GalleryInterfaces';
|
||||
import { pack, readManifest, IPackage } from './package';
|
||||
import * as tmp from 'tmp';
|
||||
import { getPublisher } from './store';
|
||||
|
@ -52,8 +52,8 @@ function readManifestFromPackage(packagePath: string): Promise<Manifest> {
|
|||
});
|
||||
}
|
||||
|
||||
function _publish(packagePath: string, pat: string, manifest: Manifest): Promise<void> {
|
||||
const api = getGalleryAPI(pat);
|
||||
async function _publish(packagePath: string, pat: string, manifest: Manifest): Promise<void> {
|
||||
const api = await getGalleryAPI(pat);
|
||||
|
||||
const packageStream = fs.createReadStream(packagePath);
|
||||
|
||||
|
@ -61,7 +61,7 @@ function _publish(packagePath: string, pat: string, manifest: Manifest): Promise
|
|||
const fullName = `${name}@${manifest.version}`;
|
||||
console.log(`Publishing ${fullName}...`);
|
||||
|
||||
return api.getExtension(manifest.publisher, manifest.name, null, ExtensionQueryFlags.IncludeVersions)
|
||||
return api.getExtension(null, manifest.publisher, manifest.name, null, ExtensionQueryFlags.IncludeVersions)
|
||||
.catch<PublishedExtension>(err => err.statusCode === 404 ? null : Promise.reject(err))
|
||||
.then(extension => {
|
||||
if (extension && extension.versions.some(v => v.version === manifest.version)) {
|
||||
|
@ -164,25 +164,6 @@ export function publish(options: IPublishOptions = {}): Promise<any> {
|
|||
});
|
||||
}
|
||||
|
||||
export function list(publisher: string): Promise<any> {
|
||||
validatePublisher(publisher);
|
||||
|
||||
return getPublisher(publisher)
|
||||
.then(p => p.pat)
|
||||
.then(getGalleryAPI)
|
||||
.then(api => {
|
||||
const criteria = [{ filterType: ExtensionQueryFilterType.InstallationTarget, value: 'Microsoft.VisualStudio.Code' }];
|
||||
const filters = [{ criteria, direction: PagingDirection.Forward, pageNumber: 0, pageSize: 1000, pagingToken: null, sortBy: SortByType.Relevance, sortOrder: SortOrderType.Default }];
|
||||
const query = { filters, flags: ExtensionQueryFlags.IncludeLatestVersionOnly | ExtensionQueryFlags.IncludeVersionProperties, assetTypes: [] };
|
||||
|
||||
return api.queryExtensions(query).then(result => {
|
||||
return result.results[0].extensions
|
||||
.filter(e => e.publisher.publisherName === publisher)
|
||||
.forEach(e => console.log(`${e.extensionName} @ ${e.versions[0].version}`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export interface IUnpublishOptions extends IPublishOptions {
|
||||
id?: string;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { getPublicGalleryAPI } from './util';
|
||||
import { ExtensionQueryFilterType } from 'vso-node-api/interfaces/GalleryInterfaces';
|
||||
import { ExtensionQueryFilterType } from 'azure-devops-node-api/interfaces/GalleryInterfaces';
|
||||
import { tableView, wordTrim } from './viewutils';
|
||||
|
||||
const pageSize = 100;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { getPublicGalleryAPI, log } from './util';
|
||||
import { ExtensionQueryFlags, PublishedExtension } from 'vso-node-api/interfaces/GalleryInterfaces';
|
||||
import { ExtensionQueryFlags, PublishedExtension } from 'azure-devops-node-api/interfaces/GalleryInterfaces';
|
||||
import { ViewTable, formatDate, formatDateTime, ratingStars, tableView, indentRow, wordWrap, icons } from './viewutils';
|
||||
|
||||
const limitVersions = 6;
|
||||
|
|
10
src/store.ts
10
src/store.ts
|
@ -60,7 +60,7 @@ async function requestPAT(store: IStore, publisherName: string): Promise<IPublis
|
|||
// If the caller of the `getRoleAssignments` API has any of the roles
|
||||
// (Creator, Owner, Contributor, Reader) on the publisher, we get a 200,
|
||||
// otherwise we get a 403.
|
||||
const api = getSecurityRolesAPI(pat);
|
||||
const api = await getSecurityRolesAPI(pat);
|
||||
await api.getRoleAssignments('gallery.publisher', publisherName);
|
||||
|
||||
return await addPublisherToStore(store, { name: publisherName, pat });
|
||||
|
@ -113,8 +113,8 @@ export function createPublisher(publisherName: string): Promise<any> {
|
|||
return read(`Publisher human-friendly name: `, { default: publisherName }).then(displayName => {
|
||||
return read(`E-mail: `).then(email => {
|
||||
return read(`Personal Access Token:`, { silent: true, replace: '*' })
|
||||
.then(pat => {
|
||||
const api = getGalleryAPI(pat);
|
||||
.then(async pat => {
|
||||
const api = await getGalleryAPI(pat);
|
||||
const raw = {
|
||||
publisherName,
|
||||
displayName,
|
||||
|
@ -127,8 +127,8 @@ export function createPublisher(publisherName: string): Promise<any> {
|
|||
emailAddress: [email]
|
||||
};
|
||||
|
||||
return api.createPublisher(raw)
|
||||
.then(() => ({ name: publisherName, pat }));
|
||||
await api.createPublisher(raw);
|
||||
return { name: publisherName, pat };
|
||||
})
|
||||
.then(publisher => load().then(store => addPublisherToStore(store, publisher)));
|
||||
});
|
||||
|
|
25
src/util.ts
25
src/util.ts
|
@ -1,10 +1,10 @@
|
|||
import * as _read from 'read';
|
||||
import { WebApi, getBasicHandler } from 'vso-node-api/WebApi';
|
||||
import { IGalleryApi } from 'vso-node-api/GalleryApi';
|
||||
import { WebApi, getPersonalAccessTokenHandler } from 'azure-devops-node-api/WebApi';
|
||||
import { IGalleryApi, GalleryApi } from 'azure-devops-node-api/GalleryApi';
|
||||
import * as denodeify from 'denodeify';
|
||||
import chalk from 'chalk';
|
||||
import { PublicGalleryAPI } from './publicgalleryapi';
|
||||
import { ISecurityRolesApi } from 'vso-node-api/SecurityRolesApi';
|
||||
import { ISecurityRolesApi } from 'azure-devops-node-api/SecurityRolesApi';
|
||||
|
||||
const __read = denodeify<_read.Options, string>(_read);
|
||||
export function read(prompt: string, options: _read.Options = {}): Promise<string> {
|
||||
|
@ -21,16 +21,19 @@ export function getPublishedUrl(extension: string): string {
|
|||
return `${marketplaceUrl}/items?itemName=${extension}`;
|
||||
}
|
||||
|
||||
export function getGalleryAPI(pat: string): IGalleryApi {
|
||||
const authHandler = getBasicHandler('oauth', pat);
|
||||
const vsoapi = new WebApi('oauth', authHandler);
|
||||
return vsoapi.getGalleryApi(marketplaceUrl);
|
||||
export async function getGalleryAPI(pat: string): Promise<IGalleryApi> {
|
||||
// from https://github.com/Microsoft/tfs-cli/blob/master/app/exec/extension/default.ts#L287-L292
|
||||
const authHandler = getPersonalAccessTokenHandler(pat);
|
||||
return new GalleryApi(marketplaceUrl, [authHandler]);
|
||||
|
||||
// const vsoapi = new WebApi(marketplaceUrl, authHandler);
|
||||
// return await vsoapi.getGalleryApi();
|
||||
}
|
||||
|
||||
export function getSecurityRolesAPI(pat: string): ISecurityRolesApi {
|
||||
const authHandler = getBasicHandler('oauth', pat);
|
||||
const vsoapi = new WebApi('oauth', authHandler);
|
||||
return vsoapi.getSecurityRolesApi(marketplaceUrl);
|
||||
export async function getSecurityRolesAPI(pat: string): Promise<ISecurityRolesApi> {
|
||||
const authHandler = getPersonalAccessTokenHandler(pat);
|
||||
const vsoapi = new WebApi(marketplaceUrl, authHandler);
|
||||
return await vsoapi.getSecurityRolesApi();
|
||||
}
|
||||
|
||||
export function getPublicGalleryAPI() {
|
||||
|
|
43
yarn.lock
43
yarn.lock
|
@ -202,6 +202,16 @@ atob@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||
|
||||
azure-devops-node-api@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/azure-devops-node-api/-/azure-devops-node-api-7.2.0.tgz#131d4e01cf12ebc6e45569b5e0c5c249e4114d6d"
|
||||
integrity sha512-pMfGJ6gAQ7LRKTHgiRF+8iaUUeGAI0c8puLaqHLc7B8AR7W6GJLozK9RFeUHFjEGybC9/EB3r67WPd7e46zQ8w==
|
||||
dependencies:
|
||||
os "0.1.1"
|
||||
tunnel "0.0.4"
|
||||
typed-rest-client "1.2.0"
|
||||
underscore "1.8.3"
|
||||
|
||||
babel-runtime@^6.9.2:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
|
@ -1592,6 +1602,11 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
os@0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/os/-/os-0.1.1.tgz#208845e89e193ad4d971474b93947736a56d13f3"
|
||||
integrity sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M=
|
||||
|
||||
osenv@^0.1.3, osenv@^0.1.4:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
|
||||
|
@ -1724,11 +1739,6 @@ pump@^3.0.0:
|
|||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
q@^1.0.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
|
||||
|
||||
randomatic@^3.0.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
|
||||
|
@ -2214,10 +2224,10 @@ tunnel@0.0.4:
|
|||
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.4.tgz#2d3785a158c174c9a16dc2c046ec5fc5f1742213"
|
||||
integrity sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=
|
||||
|
||||
typed-rest-client@^0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-0.9.0.tgz#f768cc0dc3f4e950f06e04825c36b3e7834aa1f2"
|
||||
integrity sha1-92jMDcP06VDwbgSCXDaz54NKofI=
|
||||
typed-rest-client@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-1.2.0.tgz#723085d203f38d7d147271e5ed3a75488eb44a02"
|
||||
integrity sha512-FrUshzZ1yxH8YwGR29PWWnfksLEILbWJydU7zfIRkyH7kAEzB62uMAl2WY6EyolWpLpVHeJGgQm45/MaruaHpw==
|
||||
dependencies:
|
||||
tunnel "0.0.4"
|
||||
underscore "1.8.3"
|
||||
|
@ -2237,11 +2247,6 @@ underscore@1.8.3:
|
|||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
|
||||
integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=
|
||||
|
||||
underscore@^1.8.3:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
|
||||
integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
|
||||
|
||||
union-value@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
|
||||
|
@ -2288,16 +2293,6 @@ validate-npm-package-license@^3.0.1:
|
|||
spdx-correct "^3.0.0"
|
||||
spdx-expression-parse "^3.0.0"
|
||||
|
||||
vso-node-api@6.1.2-preview:
|
||||
version "6.1.2-preview"
|
||||
resolved "https://registry.yarnpkg.com/vso-node-api/-/vso-node-api-6.1.2-preview.tgz#aab3546df2451ecd894e071bb99b5df19c5fa78f"
|
||||
integrity sha1-qrNUbfJFHs2JTgcbuZtd8Zxfp48=
|
||||
dependencies:
|
||||
q "^1.0.1"
|
||||
tunnel "0.0.4"
|
||||
typed-rest-client "^0.9.0"
|
||||
underscore "^1.8.3"
|
||||
|
||||
which-module@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||
|
|
Loading…
Add table
Reference in a new issue