assume publisher to be in package.json
This commit is contained in:
parent
8e59746de8
commit
96aea30e00
7 changed files with 155 additions and 133 deletions
95
src/login.ts
95
src/login.ts
|
@ -1,95 +0,0 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { Promise, nfcall, resolve, reject } from 'q';
|
||||
import { home } from 'osenv';
|
||||
import { read } from './util';
|
||||
import { WebApi, getBasicHandler } from 'vso-node-api/WebApi';
|
||||
|
||||
const credentialsPath = path.join(home(), '.vsce');
|
||||
|
||||
export interface ICredentials {
|
||||
account: string;
|
||||
publisher: string;
|
||||
pat: string;
|
||||
}
|
||||
|
||||
export interface IGetCredentialsOptions {
|
||||
promptToOverwrite?: boolean;
|
||||
promptIfMissing?: boolean;
|
||||
}
|
||||
|
||||
function readCredentials(): Promise<ICredentials> {
|
||||
return nfcall<string>(fs.readFile, credentialsPath, 'utf8')
|
||||
.catch<string>(err => err.code !== 'ENOENT' ? reject(err) : resolve('null'))
|
||||
.then<ICredentials>(credentialsStr => {
|
||||
try {
|
||||
return resolve(JSON.parse(credentialsStr));
|
||||
} catch (e) {
|
||||
return reject(`Error parsing credentials: ${ credentialsPath }`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeCredentials(credentials: ICredentials): Promise<ICredentials> {
|
||||
return nfcall<void>(fs.writeFile, credentialsPath, JSON.stringify(credentials))
|
||||
.then(() => credentials);
|
||||
}
|
||||
|
||||
function clearCredentials(): Promise<any> {
|
||||
return nfcall(fs.unlink, credentialsPath)
|
||||
.catch(err => err.code !== 'ENOENT' ? reject(err) : resolve('null'));
|
||||
}
|
||||
|
||||
function promptForCredentials(): Promise<ICredentials> {
|
||||
return read('Account name:').then(account => {
|
||||
if (!/^https?:\/\//.test(account)) {
|
||||
account = `https://${ account }.visualstudio.com`;
|
||||
console.log(`Assuming account name '${ account }'`);
|
||||
}
|
||||
|
||||
return read('Publisher:').then(publisher => {
|
||||
return read('Personal Access Token:', { silent: true, replace: '*' })
|
||||
.then(pat => ({ account, publisher, pat }));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getCredentials(options: IGetCredentialsOptions = {}): Promise<ICredentials> {
|
||||
return readCredentials()
|
||||
.then(credentials => {
|
||||
if (!credentials || !options.promptToOverwrite) {
|
||||
return resolve(credentials);
|
||||
}
|
||||
|
||||
console.log(`Existing credentials found: { account: ${ credentials.account }, publisher: ${ credentials.publisher } }`);
|
||||
return read('Do you want to overwrite existing credentials? [y/N] ')
|
||||
.then<ICredentials>(answer => /^y$/i.test(answer) ? null : credentials);
|
||||
})
|
||||
.then(credentials => {
|
||||
if (credentials || !options.promptIfMissing) {
|
||||
return resolve(credentials);
|
||||
}
|
||||
|
||||
return promptForCredentials()
|
||||
.then(writeCredentials);
|
||||
});
|
||||
}
|
||||
|
||||
const galleryUrl = 'https://app.market.visualstudio.com';
|
||||
|
||||
export function login(): Promise<ICredentials> {
|
||||
return getCredentials({ promptIfMissing: true, promptToOverwrite: true }).then(credentials => {
|
||||
const authHandler = getBasicHandler('oauth', credentials.pat);
|
||||
const vsoapi = new WebApi(credentials.account, authHandler);
|
||||
const api = vsoapi.getQGalleryApi(galleryUrl);
|
||||
|
||||
return api.getPublisher(credentials.publisher).then(publisher => {
|
||||
console.log(`Authentication successful. Found publisher '${ publisher.displayName }'.`);
|
||||
return credentials;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function logout(): Promise<any> {
|
||||
return clearCredentials();
|
||||
}
|
37
src/main.ts
37
src/main.ts
|
@ -2,7 +2,7 @@ import * as minimist from 'minimist';
|
|||
import { pack } from './package';
|
||||
import { publish } from './publish';
|
||||
import { fatal } from './util';
|
||||
import { login, logout } from './login';
|
||||
import { publisher } from './store';
|
||||
const packagejson = require('../package.json');
|
||||
|
||||
function helpCommand(): void {
|
||||
|
@ -11,8 +11,9 @@ function helpCommand(): void {
|
|||
Commands:
|
||||
package [vsix path] Packages the extension into a .vsix package
|
||||
publish Publishes the extension
|
||||
login Logs in to the extension service
|
||||
logout Logs out of the extension service
|
||||
publisher add [publisher] Add a publisher
|
||||
publisher rm [publisher] Remove a publisher
|
||||
publisher list List all added publishers
|
||||
|
||||
Global options:
|
||||
--help, -h Display help
|
||||
|
@ -27,22 +28,26 @@ function versionCommand(): void {
|
|||
}
|
||||
|
||||
function command(args: minimist.ParsedArgs): boolean {
|
||||
const promise = (() => {
|
||||
switch (args._[0]) {
|
||||
case 'package': return pack(args._[1]).then(({ packagePath }) => console.log(`Package created: ${ packagePath }`));
|
||||
case 'login': return login();
|
||||
case 'logout': return logout();
|
||||
case 'publish': return publish(args._[1]);
|
||||
default: return null;
|
||||
}
|
||||
})();
|
||||
try {
|
||||
const promise = (() => {
|
||||
switch (args._[0]) {
|
||||
case 'package': return pack(args._[1]).then(({ packagePath }) => console.log(`Package created: ${ packagePath }`));
|
||||
case 'publisher': return publisher(args._[1], args._[2]);
|
||||
case 'publish': return publish(args._[1]);
|
||||
default: return null;
|
||||
}
|
||||
})();
|
||||
|
||||
if (promise) {
|
||||
promise.catch(fatal);
|
||||
if (promise) {
|
||||
promise.catch(fatal);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
fatal(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = function (argv: string[]): void {
|
||||
|
|
|
@ -3,11 +3,9 @@ import * as path from 'path';
|
|||
import * as _ from 'lodash';
|
||||
import * as yazl from 'yazl';
|
||||
import { Manifest } from './manifest';
|
||||
import { getCredentials } from './login';
|
||||
import { nfcall, Promise, reject, resolve, all } from 'q';
|
||||
import * as glob from 'glob';
|
||||
import * as minimatch from 'minimatch';
|
||||
import { read } from './util';
|
||||
import { exec } from 'child_process';
|
||||
|
||||
const resourcesPath = path.join(path.dirname(__dirname), 'resources');
|
||||
|
|
|
@ -1,27 +1,21 @@
|
|||
import { readFile } from 'fs';
|
||||
import { WebApi, getBasicHandler } from 'vso-node-api/WebApi';
|
||||
import { IQGalleryApi } from 'vso-node-api/GalleryApi';
|
||||
import { ExtensionQueryFlags, PublishedExtension } from 'vso-node-api/interfaces/GalleryInterfaces';
|
||||
import { nfcall, Promise, reject, resolve, all } from 'q';
|
||||
import { pack, IPackageResult } from './package';
|
||||
import { nfcall, Promise, reject } from 'q';
|
||||
import { pack } from './package';
|
||||
import { tmpName } from 'tmp';
|
||||
import { getCredentials, ICredentials } from './login';
|
||||
import { get } from './store';
|
||||
import { getGalleryAPI } from './util';
|
||||
|
||||
const galleryUrl = 'https://app.market.visualstudio.com';
|
||||
|
||||
function getGalleryAPI({ account, pat }: ICredentials): IQGalleryApi {
|
||||
const authHandler = getBasicHandler('oauth', pat);
|
||||
const vsoapi = new WebApi(account, authHandler);
|
||||
return vsoapi.getQGalleryApi(galleryUrl);
|
||||
}
|
||||
|
||||
export function publish(cwd = process.cwd()): Promise<any> {
|
||||
return getCredentials({ promptIfMissing: true })
|
||||
.then(getGalleryAPI)
|
||||
.then(api => nfcall<string>(tmpName)
|
||||
.then(packagePath => pack(packagePath, cwd))
|
||||
.then(({ manifest, packagePath }) => nfcall<string>(readFile, packagePath, 'base64')
|
||||
.then(extensionManifest => {
|
||||
return nfcall<string>(tmpName)
|
||||
.then(packagePath => pack(packagePath, cwd))
|
||||
.then(result => {
|
||||
const { manifest, packagePath } = result;
|
||||
|
||||
return get(manifest.publisher).then(getGalleryAPI).then(api => {
|
||||
return nfcall<string>(readFile, packagePath, 'base64').then(extensionManifest => {
|
||||
const fullName = `${ manifest.name }@${ manifest.version }`;
|
||||
console.log(`Publishing ${ fullName }...`);
|
||||
|
||||
|
@ -40,7 +34,7 @@ export function publish(cwd = process.cwd()): Promise<any> {
|
|||
.catch(err => reject(err.statusCode === 409 ? `${ fullName } already exists.` : err))
|
||||
.then(() => console.log(`Successfully published ${ fullName }!`));
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
103
src/store.ts
Normal file
103
src/store.ts
Normal file
|
@ -0,0 +1,103 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { Promise, nfcall, resolve, reject } from 'q';
|
||||
import { home } from 'osenv';
|
||||
import { read, getGalleryAPI } from './util';
|
||||
import { validatePublisher } from './validation';
|
||||
|
||||
const storePath = path.join(home(), '.vsce');
|
||||
|
||||
export interface IStore {
|
||||
[publisher: string]: string;
|
||||
}
|
||||
|
||||
export interface IGetOptions {
|
||||
promptToOverwrite?: boolean;
|
||||
promptIfMissing?: boolean;
|
||||
}
|
||||
|
||||
function load(): Promise<IStore> {
|
||||
return nfcall<string>(fs.readFile, storePath, 'utf8')
|
||||
.catch<string>(err => err.code !== 'ENOENT' ? reject(err) : resolve('{}'))
|
||||
.then<IStore>(rawStore => {
|
||||
try {
|
||||
return resolve(JSON.parse(rawStore));
|
||||
} catch (e) {
|
||||
return reject(`Error parsing store: ${ storePath }`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function save(store: IStore): Promise<IStore> {
|
||||
return nfcall<void>(fs.writeFile, storePath, JSON.stringify(store))
|
||||
.then(() => store);
|
||||
}
|
||||
|
||||
function requestPAT(store: IStore, publisher: string): Promise<string> {
|
||||
return read(`Personal Access Token for publisher '${ publisher }':`, { silent: true, replace: '*' })
|
||||
.then(pat => {
|
||||
const api = getGalleryAPI(pat);
|
||||
|
||||
return api.getPublisher(publisher).then(p => {
|
||||
console.log(`Authentication successful. Found publisher '${ p.displayName }'.`);
|
||||
return pat;
|
||||
});
|
||||
})
|
||||
.then(pat => {
|
||||
store[publisher] = pat;
|
||||
return save(store).then(() => pat);
|
||||
});
|
||||
}
|
||||
|
||||
export function get(publisher: string): Promise<string> {
|
||||
validatePublisher(publisher);
|
||||
|
||||
return load().then(store => {
|
||||
if (store[publisher]) {
|
||||
return resolve(store[publisher]);
|
||||
}
|
||||
|
||||
return requestPAT(store, publisher);
|
||||
});
|
||||
}
|
||||
|
||||
function add(publisher: string): Promise<string> {
|
||||
validatePublisher(publisher);
|
||||
|
||||
return load()
|
||||
.then<IStore>(store => {
|
||||
if (store[publisher]) {
|
||||
console.log(`Publisher '${ publisher }' is already known`);
|
||||
return read('Do you want to overwrite its PAT? [y/N] ')
|
||||
.then(answer => /^y$/i.test(answer) ? store : reject('Aborted'));
|
||||
}
|
||||
|
||||
return resolve(store);
|
||||
})
|
||||
.then(store => requestPAT(store, publisher));
|
||||
}
|
||||
|
||||
function rm(publisher: string): Promise<any> {
|
||||
validatePublisher(publisher);
|
||||
|
||||
return load().then(store => {
|
||||
if (!store[publisher]) {
|
||||
return reject(`Unknown publisher '${ publisher }'`);
|
||||
}
|
||||
|
||||
delete store[publisher];
|
||||
return save(store);
|
||||
});
|
||||
}
|
||||
|
||||
function list(): Promise<string[]> {
|
||||
return load().then(store => Object.keys(store));
|
||||
}
|
||||
|
||||
export function publisher(action: string, publisher: string): Promise<any> {
|
||||
switch (action) {
|
||||
case 'add': return add(publisher);
|
||||
case 'rm': return rm(publisher);
|
||||
case 'list': default: return list().then(publishers => publishers.forEach(p => console.log(p)));
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
import { Promise, nfcall } from 'q';
|
||||
import { assign } from 'lodash';
|
||||
import _read = require('read');
|
||||
import { WebApi, getBasicHandler } from 'vso-node-api/WebApi';
|
||||
import { IQGalleryApi } from 'vso-node-api/GalleryApi';
|
||||
|
||||
export function fatal(message: any, ...args: any[]) {
|
||||
if (message instanceof Error) {
|
||||
|
@ -19,3 +21,9 @@ export function read(prompt: string, options: _read.Options = {}): Promise<strin
|
|||
return nfcall<string>(_read, assign({ prompt }, options))
|
||||
.spread(r => r);
|
||||
}
|
||||
|
||||
export function getGalleryAPI(pat: string): IQGalleryApi {
|
||||
const authHandler = getBasicHandler('oauth', pat);
|
||||
const vsoapi = new WebApi('oauth', authHandler);
|
||||
return vsoapi.getQGalleryApi('https://app.market.visualstudio.com');
|
||||
}
|
9
src/validation.ts
Normal file
9
src/validation.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export function validatePublisher(publisher: string): void {
|
||||
if (!publisher) {
|
||||
throw new Error(`Missing publisher name`);
|
||||
}
|
||||
|
||||
if (!/^[a-z0-9\-]+$/i.test(publisher)) {
|
||||
throw new Error(`Invalid publisher '${ publisher }'`);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue