Skip to content

Commit 4952226

Browse files
authoredSep 13, 2021
feat: support publishing to Open VSX (#169)
Fixes #62
1 parent 5c38553 commit 4952226

10 files changed

+439
-31
lines changed
 

‎README.md

+10
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ It is recommended to upload this to your GitHub release page so your users can e
4747

4848
If `yarn` is set to `true`, will use `--yarn`option for `vsce package` and `vsce publish`.
4949

50+
#### Publishing to OpenVSX
51+
52+
Publishing extensions to OpenVSX using this plugin is easy:
53+
54+
1. Get a valid personal access token with the correct privileges to the publisher namespace in OpenVSX. In order to get the personal access token, check this [page](https://github.com/eclipse/openvsx/wiki).
55+
56+
2. Configure the `OVSX_PAT` environment variable in your CI with the token that you created.
57+
58+
3. Enjoy! The plugin will automatically detect the environment variable and it will publish to OpenVSX, no additional configuration is needed.
59+
5060
#### Working with older versions
5161

5262
This example is for `semantic-release` v15.

‎lib/prepare.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
const execa = require('execa');
22
const { readJson } = require('fs-extra');
3+
const { isOvsxEnabled } = require('./verify-ovsx-auth');
34

45
module.exports = async (version, packageVsix, yarn, logger) => {
5-
if (packageVsix) {
6+
const ovsxEnabled = isOvsxEnabled();
7+
if (packageVsix || ovsxEnabled) {
8+
if (!packageVsix && ovsxEnabled) {
9+
logger.log('Packaging to VSIX even though `packageVsix` is not set as publish to OpenVSX is enabled.');
10+
}
11+
612
let packagePath;
713

814
if (typeof packageVsix === 'string') {

‎lib/publish.js

+25-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const execa = require('execa');
22
const { readJson } = require('fs-extra');
3+
const { isOvsxEnabled } = require('./verify-ovsx-auth');
34

45
module.exports = async (version, packagePath, yarn, logger) => {
56
const { publisher, name } = await readJson('./package.json');
@@ -20,10 +21,30 @@ module.exports = async (version, packagePath, yarn, logger) => {
2021

2122
await execa('vsce', options, { stdio: 'inherit' });
2223

23-
const url = `https://marketplace.visualstudio.com/items?itemName=${publisher}.${name}`;
24-
logger.log(`The new version is available at ${url}`);
25-
return {
24+
const vsceUrl = `https://marketplace.visualstudio.com/items?itemName=${publisher}.${name}`;
25+
logger.log(`The new version is available at ${vsceUrl}.`);
26+
27+
const vsceRelease = {
2628
name: 'Visual Studio Marketplace',
27-
url
29+
url: vsceUrl
2830
};
31+
32+
if (isOvsxEnabled()) {
33+
logger.log('Now publishing to OpenVSX');
34+
35+
await execa('ovsx', ['publish', packagePath], { stdio: 'inherit' });
36+
37+
// TODO: uncomment after https://github.com/semantic-release/semantic-release/issues/2123
38+
// const ovsxUrl = `https://open-vsx.org/extension/${publisher}/${name}/${version}`;
39+
// const ovsxRelease = {
40+
// name: 'Open VSX Registry',
41+
// url: ovsxUrl
42+
// };
43+
44+
// const releases = [vsceRelease, ovsxRelease];
45+
46+
// return releases;
47+
}
48+
49+
return vsceRelease;
2950
};

‎lib/verify-ovsx-auth.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const SemanticReleaseError = require('@semantic-release/error');
2+
3+
const isOvsxEnabled = () => {
4+
return 'OVSX_PAT' in process.env;
5+
};
6+
7+
module.exports = async (logger) => {
8+
logger.log('Verifying authentication for ovsx');
9+
10+
if (!isOvsxEnabled()) {
11+
logger.log('Skipping verification of ovsx personal token because the `OVSX_PAT` environment variable is not set.\n\nDid you know you can easily start publishing to OpenVSX with `semantic-release-vsce`?\nLearn more at https://github.com/felipecrs/semantic-release-vsce#publishing-to-openvsx');
12+
return;
13+
}
14+
15+
if (!process.env.OVSX_PAT) {
16+
throw new SemanticReleaseError('Empty ovsx personal access token specified.', 'EINVALIDOVSXPAT');
17+
}
18+
19+
// TODO: waiting for https://github.com/eclipse/openvsx/issues/313
20+
// try {
21+
// await execa('ovsx', ['verify-pat']);
22+
// } catch (e) {
23+
// throw new SemanticReleaseError(`Invalid ovsx personal access token. Additional information:\n\n${e}`, 'EINVALIDOVSXPAT');
24+
// }
25+
};
26+
27+
module.exports.isOvsxEnabled = isOvsxEnabled;

‎lib/verify.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
const verifyPkg = require('./verify-pkg');
22
const verifyAuth = require('./verify-auth');
3+
const verifyOvsxAuth = require('./verify-ovsx-auth');
34

45
module.exports = async logger => {
56
await verifyPkg();
67

78
await verifyAuth(logger);
9+
10+
await verifyOvsxAuth(logger);
811
};

‎package-lock.json

+261-19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,14 @@
4848
"preset": "conventionalcommits"
4949
},
5050
"volta": {
51-
"node": "14.17.4",
52-
"npm": "7.20.3"
51+
"node": "14.17.6",
52+
"npm": "7.23.0"
5353
},
5454
"dependencies": {
5555
"@semantic-release/error": "^2.2.0",
5656
"execa": "^5.0.0",
5757
"fs-extra": "^10.0.0",
58+
"ovsx": "^0.2.0",
5859
"vsce": "^1.96.1"
5960
},
6061
"peerDependencies": {

‎test/prepare.test.js

+27
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,30 @@ test('packageVsix is true and yarn is true', async t => {
102102
t.deepEqual(result, packagePath);
103103
t.deepEqual(execaStub.getCall(0).args, ['vsce', ['package', version, '--no-git-tag-version', '--out', packagePath, '--yarn'], { stdio: 'inherit' }]);
104104
});
105+
106+
test('packageVsix is not set but OVSX_PAT is', async t => {
107+
const { execaStub } = t.context.stubs;
108+
const name = 'test';
109+
110+
const prepare = proxyquire('../lib/prepare', {
111+
execa: execaStub,
112+
'fs-extra': {
113+
readJson: sinon.stub().returns({
114+
name
115+
})
116+
}
117+
});
118+
119+
sinon.stub(process, 'env').value({
120+
OVSX_PAT: 'abc123'
121+
});
122+
123+
const version = '1.0.0';
124+
const packageVsix = undefined;
125+
const packagePath = `${name}-${version}.vsix`;
126+
127+
const result = await prepare(version, packageVsix, undefined, logger);
128+
129+
t.deepEqual(result, packagePath);
130+
t.deepEqual(execaStub.getCall(0).args, ['vsce', ['package', version, '--no-git-tag-version', '--out', packagePath], { stdio: 'inherit' }]);
131+
});

‎test/publish.test.js

+49-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ test('publish', async t => {
3232

3333
const version = '1.0.0';
3434
const token = 'abc123';
35-
process.env.VSCE_TOKEN = token;
35+
sinon.stub(process, 'env').value({
36+
VSCE_PAT: token
37+
});
3638
const result = await publish(version, undefined, undefined, logger);
3739

3840
t.deepEqual(result, {
@@ -59,7 +61,9 @@ test('publish with packagePath', async t => {
5961
const version = '1.0.0';
6062
const packagePath = 'test.vsix';
6163
const token = 'abc123';
62-
process.env.VSCE_TOKEN = token;
64+
sinon.stub(process, 'env').value({
65+
VSCE_PAT: token
66+
});
6367
const result = await publish(version, packagePath, undefined, logger);
6468

6569
t.deepEqual(result, {
@@ -85,7 +89,9 @@ test('publish when yarn is true', async t => {
8589

8690
const version = '1.0.0';
8791
const token = 'abc123';
88-
process.env.VSCE_TOKEN = token;
92+
sinon.stub(process, 'env').value({
93+
VSCE_PAT: token
94+
});
8995
const yarn = true;
9096
const result = await publish(version, undefined, yarn, logger);
9197

@@ -112,8 +118,10 @@ test('publish with VSCE_PAT and VSCE_TOKEN should prefer VSCE_PAT', async t => {
112118

113119
const version = '1.0.0';
114120
const token = 'abc123';
115-
process.env.VSCE_TOKEN = token;
116-
process.env.VSCE_PAT = token;
121+
sinon.stub(process, 'env').value({
122+
VSCE_PAT: token,
123+
VSCE_TOKEN: token
124+
});
117125
const result = await publish(version, undefined, undefined, logger);
118126

119127
t.deepEqual(result, {
@@ -122,3 +130,39 @@ test('publish with VSCE_PAT and VSCE_TOKEN should prefer VSCE_PAT', async t => {
122130
});
123131
t.deepEqual(execaStub.getCall(0).args, ['vsce', ['publish', version, '--no-git-tag-version'], { stdio: 'inherit' }]);
124132
});
133+
134+
test('publish to OpenVSX', async t => {
135+
const { execaStub } = t.context.stubs;
136+
const publisher = 'semantic-release-vsce';
137+
const name = 'Semantice Release VSCE';
138+
const publish = proxyquire('../lib/publish', {
139+
execa: execaStub,
140+
'fs-extra': {
141+
readJson: sinon.stub().returns({
142+
publisher,
143+
name
144+
})
145+
}
146+
});
147+
148+
const version = '1.0.0';
149+
const packagePath = 'test.vsix';
150+
const token = 'abc123';
151+
sinon.stub(process, 'env').value({
152+
OVSX_PAT: token,
153+
VSCE_TOKEN: token
154+
});
155+
const result = await publish(version, packagePath, undefined, logger);
156+
157+
t.deepEqual(result, {
158+
name: 'Visual Studio Marketplace',
159+
url: `https://marketplace.visualstudio.com/items?itemName=${publisher}.${name}`
160+
});
161+
t.deepEqual(execaStub.getCall(0).args, ['vsce', ['publish', '--packagePath', packagePath], { stdio: 'inherit' }]);
162+
163+
// t.deepEqual(result[1], {
164+
// name: 'Open VSX Registry',
165+
// url: `https://open-vsx.org/extension/${publisher}/${name}/${version}`
166+
// });
167+
t.deepEqual(execaStub.getCall(1).args, ['ovsx', ['publish', packagePath], { stdio: 'inherit' }]);
168+
});

‎test/verify-ovsx-auth.test.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const test = require('ava');
2+
const sinon = require('sinon');
3+
const SemanticReleaseError = require('@semantic-release/error');
4+
5+
const logger = {
6+
log: sinon.fake()
7+
};
8+
9+
test('OVSX_PAT is set', async t => {
10+
sinon.stub(process, 'env').value({
11+
OVSX_PAT: 'abc123'
12+
});
13+
14+
const verifyOvsxAuth = require('../lib/verify-ovsx-auth');
15+
16+
await t.notThrowsAsync(() => verifyOvsxAuth(logger));
17+
});
18+
19+
test('OVSX_PAT is invalid', async t => {
20+
sinon.stub(process, 'env').value({
21+
OVSX_PAT: ''
22+
});
23+
24+
const verifyOvsxAuth = require('../lib/verify-ovsx-auth');
25+
26+
await t.throwsAsync(() => verifyOvsxAuth(logger), { instanceOf: SemanticReleaseError, code: 'EINVALIDOVSXPAT' });
27+
});

0 commit comments

Comments
 (0)
Please sign in to comment.