ovsx 0.9.5 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +14 -0
- package/lib/check-license.js +2 -3
- package/lib/check-license.js.map +1 -1
- package/lib/create-namespace.d.ts.map +1 -1
- package/lib/create-namespace.js +2 -5
- package/lib/create-namespace.js.map +1 -1
- package/lib/get.js +1 -2
- package/lib/get.js.map +1 -1
- package/lib/login.d.ts +8 -1
- package/lib/login.d.ts.map +1 -1
- package/lib/login.js +19 -2
- package/lib/login.js.map +1 -1
- package/lib/logout.d.ts +2 -0
- package/lib/logout.d.ts.map +1 -0
- package/lib/logout.js +16 -0
- package/lib/logout.js.map +1 -0
- package/lib/main.js +20 -5
- package/lib/main.js.map +1 -1
- package/lib/ovsx +2 -2
- package/lib/publish.d.ts +4 -0
- package/lib/publish.d.ts.map +1 -1
- package/lib/publish.js +8 -6
- package/lib/publish.js.map +1 -1
- package/lib/registry.d.ts +0 -2
- package/lib/registry.d.ts.map +1 -1
- package/lib/registry.js +1 -4
- package/lib/registry.js.map +1 -1
- package/lib/store.d.ts +39 -0
- package/lib/store.d.ts.map +1 -0
- package/lib/store.js +115 -0
- package/lib/store.js.map +1 -0
- package/lib/util.d.ts +5 -3
- package/lib/util.d.ts.map +1 -1
- package/lib/util.js +39 -15
- package/lib/util.js.map +1 -1
- package/lib/verify-pat.d.ts +1 -0
- package/lib/verify-pat.d.ts.map +1 -1
- package/lib/verify-pat.js +10 -7
- package/lib/verify-pat.js.map +1 -1
- package/lib/version.d.ts +2 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +5 -0
- package/lib/version.js.map +1 -0
- package/lib/zip.d.ts +4 -0
- package/lib/zip.d.ts.map +1 -0
- package/lib/zip.js +48 -0
- package/lib/zip.js.map +1 -0
- package/package.json +18 -11
- package/src/create-namespace.ts +3 -4
- package/src/login.ts +28 -1
- package/src/logout.ts +15 -0
- package/src/main.ts +23 -7
- package/src/ovsx +2 -2
- package/src/publish.ts +23 -16
- package/src/registry.ts +1 -5
- package/src/store.ts +142 -0
- package/src/util.ts +30 -0
- package/src/verify-pat.ts +22 -19
- package/src/version.ts +1 -0
- package/src/zip.ts +55 -0
package/src/main.ts
CHANGED
|
@@ -15,8 +15,9 @@ import { verifyPat } from './verify-pat';
|
|
|
15
15
|
import { publish } from './publish';
|
|
16
16
|
import { handleError } from './util';
|
|
17
17
|
import { getExtension } from './get';
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
import login from './login';
|
|
19
|
+
import logout from './logout';
|
|
20
|
+
import { LIB_VERSION } from './version';
|
|
20
21
|
|
|
21
22
|
module.exports = function (argv: string[]): void {
|
|
22
23
|
const program = new commander.Command();
|
|
@@ -24,7 +25,7 @@ module.exports = function (argv: string[]): void {
|
|
|
24
25
|
.option('-r, --registryUrl <url>', 'Use the registry API at this base URL.')
|
|
25
26
|
.option('-p, --pat <token>', 'Personal access token.')
|
|
26
27
|
.option('--debug', 'Include debug information on error')
|
|
27
|
-
.version(
|
|
28
|
+
.version(LIB_VERSION, '-V, --version', 'Print the Eclipse Open VSX CLI version');
|
|
28
29
|
|
|
29
30
|
const createNamespaceCmd = program.command('create-namespace <name>');
|
|
30
31
|
createNamespaceCmd.description('Create a new namespace')
|
|
@@ -52,7 +53,8 @@ module.exports = function (argv: string[]): void {
|
|
|
52
53
|
.option('--pre-release', 'Mark this package as a pre-release')
|
|
53
54
|
.option('--no-dependencies', 'Disable dependency detection via npm or yarn')
|
|
54
55
|
.option('--skip-duplicate', 'Fail silently if version already exists on the marketplace')
|
|
55
|
-
.
|
|
56
|
+
.option('--packageVersion <version>', 'Version of the provided VSIX packages.')
|
|
57
|
+
.action((extensionFile: string, { target, packagePath, baseContentUrl, baseImagesUrl, yarn, preRelease, dependencies, skipDuplicate, packageVersion }) => {
|
|
56
58
|
if (extensionFile !== undefined && packagePath !== undefined) {
|
|
57
59
|
console.error('\u274c Please specify either a package file or a package path, but not both.\n');
|
|
58
60
|
publishCmd.help();
|
|
@@ -67,16 +69,17 @@ module.exports = function (argv: string[]): void {
|
|
|
67
69
|
console.warn("Ignoring option '--baseImagesUrl' for prepackaged extension.");
|
|
68
70
|
if (extensionFile !== undefined && yarn !== undefined)
|
|
69
71
|
console.warn("Ignoring option '--yarn' for prepackaged extension.");
|
|
72
|
+
if (extensionFile !== undefined && packageVersion !== undefined)
|
|
73
|
+
console.warn("Ignoring option '--packageVersion' for prepackaged extension.");
|
|
70
74
|
const { registryUrl, pat } = program.opts();
|
|
71
|
-
publish({ extensionFile, registryUrl, pat, targets: typeof target === 'string' ? [target] : target, packagePath: typeof packagePath === 'string' ? [packagePath] : packagePath, baseContentUrl, baseImagesUrl, yarn, preRelease, dependencies, skipDuplicate })
|
|
75
|
+
publish({ extensionFile, registryUrl, pat, targets: typeof target === 'string' ? [target] : target, packagePath: typeof packagePath === 'string' ? [packagePath] : packagePath, baseContentUrl, baseImagesUrl, yarn, preRelease, dependencies, skipDuplicate, packageVersion })
|
|
72
76
|
.then(results => {
|
|
73
77
|
const reasons = results.filter(result => result.status === 'rejected')
|
|
74
|
-
.map(result => result as PromiseRejectedResult)
|
|
75
78
|
.map(rejectedResult => rejectedResult.reason);
|
|
76
79
|
|
|
77
80
|
if (reasons.length > 0) {
|
|
78
81
|
const message = 'See the documentation for more information:\n'
|
|
79
|
-
|
|
82
|
+
+ 'https://github.com/eclipse/openvsx/wiki/Publishing-Extensions';
|
|
80
83
|
const errorHandler = handleError(program.debug, message, false);
|
|
81
84
|
for (const reason of reasons) {
|
|
82
85
|
errorHandler(reason);
|
|
@@ -99,6 +102,19 @@ module.exports = function (argv: string[]): void {
|
|
|
99
102
|
.catch(handleError(program.debug));
|
|
100
103
|
});
|
|
101
104
|
|
|
105
|
+
const loginCmd = program.command('login <namespace>');
|
|
106
|
+
loginCmd.description('Adds a namespace to the list of known namespaces')
|
|
107
|
+
.action((namespace: string) => {
|
|
108
|
+
const { registryUrl, pat } = program.opts();
|
|
109
|
+
login({ namespace, registryUrl, pat }).catch(handleError(program.debug));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const logoutCmd = program.command('logout <namespace>');
|
|
113
|
+
logoutCmd.description('Removes a namespace from the list of known namespaces')
|
|
114
|
+
.action((namespace: string) => {
|
|
115
|
+
logout(namespace).catch(handleError(program.debug));
|
|
116
|
+
});
|
|
117
|
+
|
|
102
118
|
program
|
|
103
119
|
.command('*', '', { noHelp: true })
|
|
104
120
|
.action((cmd: commander.Command) => {
|
package/src/ovsx
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const semver = require('semver');
|
|
4
4
|
|
|
5
|
-
if (semver.lt(process.versions.node, '
|
|
6
|
-
console.error('ovsx requires at least NodeJS version
|
|
5
|
+
if (semver.lt(process.versions.node, '20.0.0')) {
|
|
6
|
+
console.error('ovsx requires at least NodeJS version 20. Check your installed version with `node --version`.');
|
|
7
7
|
process.exit(1);
|
|
8
8
|
}
|
|
9
9
|
|
package/src/publish.ts
CHANGED
|
@@ -8,32 +8,29 @@
|
|
|
8
8
|
* SPDX-License-Identifier: EPL-2.0
|
|
9
9
|
********************************************************************************/
|
|
10
10
|
import { createVSIX, IPackageOptions } from '@vscode/vsce';
|
|
11
|
-
import { createTempFile, addEnvOptions } from './util';
|
|
11
|
+
import { createTempFile, addEnvOptions, getPAT } from './util';
|
|
12
12
|
import { Extension, Registry, RegistryOptions } from './registry';
|
|
13
13
|
import { checkLicense } from './check-license';
|
|
14
|
+
import { readVSIXPackage } from './zip';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Publishes an extension.
|
|
17
18
|
*/
|
|
18
19
|
export async function publish(options: PublishOptions = {}): Promise<PromiseSettledResult<void>[]> {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
20
|
+
addEnvOptions(options);
|
|
21
|
+
const internalPublishOptions: InternalPublishOptions[] = [];
|
|
22
|
+
const packagePaths = options.packagePath || [undefined];
|
|
23
|
+
const targets = options.targets || [undefined];
|
|
24
|
+
for (const packagePath of packagePaths) {
|
|
25
|
+
for (const target of targets) {
|
|
26
|
+
internalPublishOptions.push({ ...options, packagePath: packagePath, target: target });
|
|
27
27
|
}
|
|
28
|
+
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
return Promise.allSettled(internalPublishOptions.map(publishOptions => doPublish(publishOptions)));
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
async function doPublish(options: InternalPublishOptions = {}): Promise<void> {
|
|
33
|
-
if (!options.pat) {
|
|
34
|
-
throw new Error("A personal access token must be given with the option '--pat'.");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
34
|
// if the packagePath is a link to a vsix, don't need to package it
|
|
38
35
|
if (options.packagePath?.endsWith('.vsix')) {
|
|
39
36
|
options.extensionFile = options.packagePath;
|
|
@@ -48,6 +45,11 @@ async function doPublish(options: InternalPublishOptions = {}): Promise<void> {
|
|
|
48
45
|
console.warn("Ignoring option '--pre-release' for prepackaged extension.");
|
|
49
46
|
}
|
|
50
47
|
|
|
48
|
+
if (!options.pat) {
|
|
49
|
+
const namespace = (await readVSIXPackage(options.extensionFile!)).publisher;
|
|
50
|
+
options.pat = await getPAT(namespace, options);
|
|
51
|
+
}
|
|
52
|
+
|
|
51
53
|
let extension: Extension | undefined;
|
|
52
54
|
try {
|
|
53
55
|
extension = await registry.publish(options.extensionFile!, options.pat);
|
|
@@ -100,6 +102,10 @@ interface PublishCommonOptions extends RegistryOptions {
|
|
|
100
102
|
* Whether to fail silently if version already exists on the marketplace
|
|
101
103
|
*/
|
|
102
104
|
skipDuplicate?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Extension version. Only valid with `packagePath`.
|
|
107
|
+
*/
|
|
108
|
+
packageVersion?: string;
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
// Interface used by top level CLI
|
|
@@ -141,7 +147,7 @@ interface InternalPublishOptions extends PublishCommonOptions {
|
|
|
141
147
|
/**
|
|
142
148
|
* Whether to do dependency detection via npm or yarn
|
|
143
149
|
*/
|
|
144
|
-
|
|
150
|
+
dependencies?: boolean;
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
async function packageExtension(options: InternalPublishOptions, registry: Registry): Promise<void> {
|
|
@@ -158,7 +164,8 @@ async function packageExtension(options: InternalPublishOptions, registry: Regis
|
|
|
158
164
|
baseImagesUrl: options.baseImagesUrl,
|
|
159
165
|
useYarn: options.yarn,
|
|
160
166
|
dependencies: options.dependencies,
|
|
161
|
-
preRelease: options.preRelease
|
|
167
|
+
preRelease: options.preRelease,
|
|
168
|
+
version: options.packageVersion
|
|
162
169
|
};
|
|
163
170
|
await createVSIX(packageOptions);
|
|
164
171
|
}
|
package/src/registry.ts
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
********************************************************************************/
|
|
10
10
|
|
|
11
11
|
import * as http from 'http';
|
|
12
|
-
import * as https from 'https';
|
|
13
12
|
import * as fs from 'fs';
|
|
14
13
|
import * as querystring from 'querystring';
|
|
15
14
|
import * as followRedirects from 'follow-redirects';
|
|
@@ -167,10 +166,7 @@ export class Registry {
|
|
|
167
166
|
}
|
|
168
167
|
|
|
169
168
|
private getProtocol(url: URL) {
|
|
170
|
-
|
|
171
|
-
return followRedirects.https as typeof https;
|
|
172
|
-
else
|
|
173
|
-
return followRedirects.http as typeof http;
|
|
169
|
+
return url.protocol === 'https:' ? followRedirects.https : followRedirects.http;
|
|
174
170
|
}
|
|
175
171
|
|
|
176
172
|
private getRequestOptions(method?: string, headers?: http.OutgoingHttpHeaders, maxBodyLength?: number): http.RequestOptions {
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
|
|
5
|
+
interface StoreEntry {
|
|
6
|
+
name: string
|
|
7
|
+
value: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Store extends Iterable<StoreEntry> {
|
|
11
|
+
readonly size: number;
|
|
12
|
+
get(name: string): string | undefined;
|
|
13
|
+
add(name: string, value: string): Promise<void>;
|
|
14
|
+
delete(name: string): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class FileStore implements Store {
|
|
18
|
+
private static readonly DefaultPath = path.join(homedir(), '.ovsx');
|
|
19
|
+
|
|
20
|
+
static async open(path: string = FileStore.DefaultPath): Promise<FileStore> {
|
|
21
|
+
try {
|
|
22
|
+
const rawStore = await fs.promises.readFile(path, 'utf8');
|
|
23
|
+
return new FileStore(path, JSON.parse(rawStore).entries);
|
|
24
|
+
} catch (err: any) {
|
|
25
|
+
if (err.code === 'ENOENT') {
|
|
26
|
+
return new FileStore(path, []);
|
|
27
|
+
} else if (/SyntaxError/.test(err)) {
|
|
28
|
+
throw new Error(`Error parsing file store: ${path}.`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get size(): number {
|
|
36
|
+
return this.entries.length;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private constructor(readonly path: string, private entries: StoreEntry[]) { }
|
|
40
|
+
|
|
41
|
+
private async save(): Promise<void> {
|
|
42
|
+
await fs.promises.writeFile(this.path, JSON.stringify({ entries: this.entries }), { mode: '0600' });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async deleteStore(): Promise<void> {
|
|
46
|
+
try {
|
|
47
|
+
await fs.promises.unlink(this.path);
|
|
48
|
+
} catch {
|
|
49
|
+
// noop
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get(name: string): string | undefined {
|
|
54
|
+
return this.entries.find(p => p.name === name)?.value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async add(name: string, value: string): Promise<void> {
|
|
58
|
+
const newEntry: StoreEntry = { name, value };
|
|
59
|
+
this.entries = [...this.entries.filter(p => p.name !== name), newEntry];
|
|
60
|
+
await this.save();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async delete(name: string): Promise<void> {
|
|
64
|
+
this.entries = this.entries.filter(p => p.name !== name);
|
|
65
|
+
await this.save();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[Symbol.iterator]() {
|
|
69
|
+
return this.entries[Symbol.iterator]();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class KeytarStore implements Store {
|
|
74
|
+
static async open(serviceName = 'ovsx'): Promise<KeytarStore> {
|
|
75
|
+
const keytar = await import('keytar');
|
|
76
|
+
const creds = await keytar.findCredentials(serviceName);
|
|
77
|
+
|
|
78
|
+
return new KeytarStore(
|
|
79
|
+
keytar,
|
|
80
|
+
serviceName,
|
|
81
|
+
creds.map(({ account, password }) => ({ name: account, value: password }))
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get size(): number {
|
|
86
|
+
return this.entries.length;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private constructor(
|
|
90
|
+
private readonly keytar: typeof import('keytar'),
|
|
91
|
+
private readonly serviceName: string,
|
|
92
|
+
private entries: StoreEntry[]
|
|
93
|
+
) { }
|
|
94
|
+
|
|
95
|
+
get(name: string): string | undefined {
|
|
96
|
+
return this.entries.find(p => p.name === name)?.value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async add(name: string, value: string): Promise<void> {
|
|
100
|
+
const newEntry: StoreEntry = { name, value };
|
|
101
|
+
this.entries = [...this.entries.filter(p => p.name !== name), newEntry];
|
|
102
|
+
await this.keytar.setPassword(this.serviceName, name, value);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async delete(name: string): Promise<void> {
|
|
106
|
+
this.entries = this.entries.filter(p => p.name !== name);
|
|
107
|
+
await this.keytar.deletePassword(this.serviceName, name);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[Symbol.iterator](): Iterator<StoreEntry, any, undefined> {
|
|
111
|
+
return this.entries[Symbol.iterator]();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function openDefaultStore(): Promise<Store> {
|
|
116
|
+
if (/^file$/i.test(process.env['OVSX_STORE'] ?? '')) {
|
|
117
|
+
return await FileStore.open();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let keytarStore: Store;
|
|
121
|
+
try {
|
|
122
|
+
keytarStore = await KeytarStore.open();
|
|
123
|
+
} catch (err) {
|
|
124
|
+
const store = await FileStore.open();
|
|
125
|
+
console.warn(`Failed to open credential store. Falling back to storing secrets clear-text in: ${store.path}.`);
|
|
126
|
+
return store;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const fileStore = await FileStore.open();
|
|
130
|
+
|
|
131
|
+
// migrate from file store
|
|
132
|
+
if (fileStore.size) {
|
|
133
|
+
for (const { name, value } of fileStore) {
|
|
134
|
+
await keytarStore.add(name, value);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await fileStore.deleteStore();
|
|
138
|
+
console.info(`Migrated ${fileStore.size} publishers to system credential manager. Deleted local store '${fileStore.path}'.`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return keytarStore;
|
|
142
|
+
}
|
package/src/util.ts
CHANGED
|
@@ -14,6 +14,10 @@ import * as tmp from 'tmp';
|
|
|
14
14
|
import * as http from 'http';
|
|
15
15
|
import * as readline from 'readline';
|
|
16
16
|
import { RegistryOptions } from './registry';
|
|
17
|
+
import { VerifyPatOptions, doVerifyPat } from './verify-pat';
|
|
18
|
+
import { PublishOptions } from './publish';
|
|
19
|
+
import { openDefaultStore } from './store';
|
|
20
|
+
import { CreateNamespaceOptions } from './create-namespace';
|
|
17
21
|
|
|
18
22
|
export { promisify } from 'util';
|
|
19
23
|
|
|
@@ -183,3 +187,29 @@ export async function getUserChoice<R extends string>(text: string, values: R[],
|
|
|
183
187
|
}
|
|
184
188
|
return defaultValue;
|
|
185
189
|
}
|
|
190
|
+
|
|
191
|
+
export async function requestPAT(namespace: string, options: CreateNamespaceOptions | PublishOptions | VerifyPatOptions, verify: boolean = true): Promise<string> {
|
|
192
|
+
const pat = await getUserInput(`Personal Access Token for namespace '${namespace}':`);
|
|
193
|
+
if (verify) {
|
|
194
|
+
await doVerifyPat({ ...options, namespace, pat });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return pat;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export async function getPAT(namespace: string, options: CreateNamespaceOptions | PublishOptions | VerifyPatOptions, verify: boolean = true): Promise<string> {
|
|
201
|
+
if (options?.pat) {
|
|
202
|
+
return options.pat;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const store = await openDefaultStore();
|
|
206
|
+
let pat = store.get(namespace);
|
|
207
|
+
if (pat) {
|
|
208
|
+
return pat;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
pat = await requestPAT(namespace, options, verify);
|
|
212
|
+
await store.add(namespace, pat);
|
|
213
|
+
|
|
214
|
+
return pat;
|
|
215
|
+
}
|
package/src/verify-pat.ts
CHANGED
|
@@ -9,39 +9,42 @@
|
|
|
9
9
|
********************************************************************************/
|
|
10
10
|
|
|
11
11
|
import { Registry, RegistryOptions } from './registry';
|
|
12
|
-
import { readManifest, addEnvOptions } from './util';
|
|
12
|
+
import { readManifest, addEnvOptions, getPAT } from './util';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Validates that a Personal Access Token can publish to a namespace.
|
|
16
16
|
*/
|
|
17
17
|
export async function verifyPat(options: VerifyPatOptions): Promise<void> {
|
|
18
18
|
addEnvOptions(options);
|
|
19
|
-
if (!options.pat) {
|
|
20
|
-
throw new Error("A personal access token must be given with the option '--pat'.");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
19
|
if (!options.namespace) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
let error;
|
|
21
|
+
try {
|
|
22
|
+
options.namespace = (await readManifest()).publisher;
|
|
23
|
+
} catch (e) {
|
|
24
|
+
error = e;
|
|
25
|
+
}
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
if (!options.namespace) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Unable to read the namespace's name. Please supply it as an argument or run ovsx from the extension folder.` +
|
|
30
|
+
(error ? `\n\n${error}` : '')
|
|
31
|
+
);
|
|
32
|
+
}
|
|
37
33
|
}
|
|
38
34
|
|
|
35
|
+
options.pat = await getPAT(options.namespace, options, false);
|
|
36
|
+
await doVerifyPat(options);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function doVerifyPat(options: VerifyPatOptions) {
|
|
39
40
|
const registry = new Registry(options);
|
|
40
|
-
const
|
|
41
|
+
const namespace = options.namespace as string;
|
|
42
|
+
const pat = options.pat as string;
|
|
43
|
+
const result = await registry.verifyPat(namespace, pat);
|
|
41
44
|
if (result.error) {
|
|
42
45
|
throw new Error(result.error);
|
|
43
46
|
}
|
|
44
|
-
console.log(`\ud83d\ude80 PAT valid to publish at ${
|
|
47
|
+
console.log(`\ud83d\ude80 PAT valid to publish at ${namespace}`);
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
export interface VerifyPatOptions extends RegistryOptions {
|
package/src/version.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const LIB_VERSION = "0.10.1";
|
package/src/zip.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Entry, open, ZipFile } from 'yauzl';
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
import { Manifest } from './util';
|
|
4
|
+
|
|
5
|
+
async function bufferStream(stream: Readable): Promise<Buffer> {
|
|
6
|
+
return await new Promise((resolve, reject) => {
|
|
7
|
+
const buffers: Buffer[] = [];
|
|
8
|
+
stream.on('data', buffer => buffers.push(buffer));
|
|
9
|
+
stream.once('error', reject);
|
|
10
|
+
stream.once('end', () => resolve(Buffer.concat(buffers)));
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function readZip(packagePath: string, filter: (name: string) => boolean): Promise<Map<string, Buffer>> {
|
|
15
|
+
const zipfile = await new Promise<ZipFile>((resolve, reject) =>
|
|
16
|
+
open(packagePath, { lazyEntries: true }, (err, zipfile) => (err ? reject(err) : resolve(zipfile)))
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return await new Promise((resolve, reject) => {
|
|
20
|
+
const result = new Map<string, Buffer>();
|
|
21
|
+
|
|
22
|
+
zipfile.once('close', () => resolve(result));
|
|
23
|
+
|
|
24
|
+
zipfile.readEntry();
|
|
25
|
+
zipfile.on('entry', (entry: Entry) => {
|
|
26
|
+
const name = entry.fileName.toLowerCase();
|
|
27
|
+
|
|
28
|
+
if (filter(name)) {
|
|
29
|
+
zipfile.openReadStream(entry, (err, stream) => {
|
|
30
|
+
if (err) {
|
|
31
|
+
zipfile.close();
|
|
32
|
+
return reject(err);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
bufferStream(stream).then(buffer => {
|
|
36
|
+
result.set(name, buffer);
|
|
37
|
+
zipfile.readEntry();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
zipfile.readEntry();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function readVSIXPackage(packagePath: string): Promise<Manifest> {
|
|
48
|
+
const map = await readZip(packagePath, name => /^extension\/package\.json$/i.test(name));
|
|
49
|
+
const rawManifest = map.get('extension/package.json');
|
|
50
|
+
if (!rawManifest) {
|
|
51
|
+
throw new Error('Manifest not found.');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return JSON.parse(rawManifest.toString('utf8')) as Manifest;
|
|
55
|
+
}
|