Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow to publish to Open VSX only #488

Merged
merged 5 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Which `.vsix` file (or files) to publish. This controls what value will be used

| Variable | Description |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VSCE_PAT` | **Required** (unless `publish` is set to `false`). The personal access token to publish the extension to Visual Studio Marketplace |
| `VSCE_PAT` | **Required** (unless `publish` is set to `false`, or the `OVSX_PAT` environment variable is set). The personal access token to publish the extension to Visual Studio Marketplace |
dankeboy36 marked this conversation as resolved.
Show resolved Hide resolved
| `VSCE_TARGET` | _Optional_. The target to use when packaging or publishing the extension (used as `vsce package --target ${VSCE_TARGET}`). When set to `universal`, behave as if `VSCE_TARGET` was not set (i.e. build the universal/generic `vsix`). See [the platform-specific example](#platform-specific-on-github-actions) |
| `OVSX_PAT` | _Optional_. The personal access token to push to OpenVSX |

Expand All @@ -104,7 +104,7 @@ You can set `vsce` options in the `package.json`, like:
// package.json
{
"vsce": {
"baseImagesUrl": "https://my.custom/base/images/url"
"baseImagesUrl": "https://my.custom/base/images/url",
"dependencies": true,
"yarn": false
}
Expand All @@ -125,7 +125,7 @@ Publishing extensions to OpenVSX using this plugin is easy:

## Examples

### Github Actions
### GitHub Actions

```yaml
name: release
Expand Down
54 changes: 31 additions & 23 deletions lib/publish.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
// @ts-check

const SemanticReleaseError = require('@semantic-release/error');
const execa = require('execa');
const { readJson } = require('fs-extra');
const { isOvsxEnabled, isTargetEnabled } = require('./utils');
const { isOvsxEnabled, isTargetEnabled, isVsceEnabled } = require('./utils');

module.exports = async (version, packagePath, logger, cwd) => {
const vsceEnabled = isVsceEnabled();
const ovsxEnabled = isOvsxEnabled();
if (!vsceEnabled && !ovsxEnabled) {
throw new SemanticReleaseError(
'Could not detect the `VSCE_PAT` or `OVSX_PAT` environment variable. At least one of them must present to publish the VSIX.',
'ENOPAT',
);
}
dankeboy36 marked this conversation as resolved.
Show resolved Hide resolved

const { publisher, name } = await readJson('./package.json');

const options = ['publish'];
Expand All @@ -27,38 +37,36 @@ module.exports = async (version, packagePath, logger, cwd) => {
message += ` for target ${process.env.VSCE_TARGET}`;
}
}
message += ' to Visual Studio Marketplace';
logger.log(message);

await execa('vsce', options, { stdio: 'inherit', preferLocal: true, cwd });
const releases = [];
if (vsceEnabled) {
logger.log(message + ' to Visual Studio Marketplace');

const vsceUrl = `https://marketplace.visualstudio.com/items?itemName=${publisher}.${name}`;
logger.log(`The new version is available at ${vsceUrl}.`);
await execa('vsce', options, { stdio: 'inherit', preferLocal: true, cwd });
const vsceUrl = `https://marketplace.visualstudio.com/items?itemName=${publisher}.${name}`;

const vsceRelease = {
name: 'Visual Studio Marketplace',
url: vsceUrl,
};
logger.log(`The new version is available at ${vsceUrl}.`);
releases.push({
name: 'Visual Studio Marketplace',
url: vsceUrl,
});
}

if (isOvsxEnabled()) {
logger.log('Now publishing to OpenVSX');
if (ovsxEnabled) {
logger.log(message + 'to Open VSX Registry');

// When publishing to OpenVSX, packagePath will be always set
await execa('ovsx', options, { stdio: 'inherit', preferLocal: true, cwd });
const ovsxUrl = `https://open-vsx.org/extension/${publisher}/${name}/${version}`;

logger.log(`The new ovsx version is available at ${ovsxUrl}`);

// TODO: uncomment after https://github.com/semantic-release/semantic-release/issues/2123
// const ovsxRelease = {
// name: 'Open VSX Registry',
// url: ovsxUrl
// };

// const releases = [vsceRelease, ovsxRelease];

// return releases;
releases.push({
name: 'Open VSX Registry',
url: ovsxUrl,
});
}

return vsceRelease;
// TODO: uncomment after https://github.com/semantic-release/semantic-release/issues/2123
// return releases;
return releases.shift();
};
7 changes: 7 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
// @ts-check

const isOvsxEnabled = () => {
return 'OVSX_PAT' in process.env;
};

const isVsceEnabled = () => {
dankeboy36 marked this conversation as resolved.
Show resolved Hide resolved
return 'VSCE_PAT' in process.env;
};

const isTargetEnabled = () => {
return (
'VSCE_TARGET' in process.env && process.env.VSCE_TARGET !== 'universal'
Expand All @@ -11,4 +17,5 @@ const isTargetEnabled = () => {
module.exports = {
isTargetEnabled,
isOvsxEnabled,
isVsceEnabled,
};
2 changes: 1 addition & 1 deletion lib/verify-pkg.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = async () => {

try {
packageJson = await readJson('./package.json');
} catch (error) {
} catch {
throw new SemanticReleaseError(
'The `package.json` seems to be invalid.',
'EINVALIDPKG',
Expand Down
18 changes: 17 additions & 1 deletion lib/verify.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
// @ts-check

const SemanticReleaseError = require('@semantic-release/error');
const verifyPkg = require('./verify-pkg');
const verifyAuth = require('./verify-auth');
const verifyOvsxAuth = require('./verify-ovsx-auth');
const verifyTarget = require('./verify-target');
const { isOvsxEnabled } = require('./utils');

module.exports = async (pluginConfig, { logger, cwd }) => {
await verifyPkg();
await verifyTarget();

if (pluginConfig?.publish !== false) {
await verifyAuth(logger, cwd);
try {
await verifyAuth(logger, cwd);
dankeboy36 marked this conversation as resolved.
Show resolved Hide resolved
} catch (err) {
if (
err instanceof SemanticReleaseError &&
err.code === 'ENOVSCEPAT' &&
isOvsxEnabled()
) {
logger.log(
'The vsce personal access token is missing (set the `VSCE_PAT` environment variable) but publish to OpenVSX is enabled.',
);
} else {
throw err;
}
}
dankeboy36 marked this conversation as resolved.
Show resolved Hide resolved
await verifyOvsxAuth(logger, cwd);
}
};
28 changes: 27 additions & 1 deletion test/index.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const test = require('ava');
const test = require('ava').serial;
const sinon = require('sinon');
const proxyquire = require('proxyquire');

Expand Down Expand Up @@ -174,6 +174,32 @@ test('it does not publish the package if publishing is disabled', async (t) => {
t.true(vscePublishStub.notCalled);
});

test('it can publish when `OVSX_PAT` is present but `VSCE_PAT` is missing', async (t) => {
const token = 'abc123';
sinon.stub(process, 'env').value({
OVSX_PAT: token,
});
const { verifyVsceStub, vscePrepareStub, vscePublishStub } = t.context.stubs;
const { prepare, publish, verifyConditions } = proxyquire('../index.js', {
'./lib/verify': verifyVsceStub,
'./lib/publish': vscePublishStub,
'./lib/prepare': vscePrepareStub,
});

await verifyConditions({ ...pluginConfig }, semanticReleasePayload);
await prepare({ ...pluginConfig }, semanticReleasePayload);
await publish({ ...pluginConfig }, semanticReleasePayload);

t.true(verifyVsceStub.calledOnce);
t.true(vscePrepareStub.calledOnce);
t.deepEqual(vscePublishStub.getCall(0).args, [
semanticReleasePayload.nextRelease.version,
undefined,
semanticReleasePayload.logger,
semanticReleasePayload.cwd,
]);
});

test('expand globs if publishPackagePath is set', async (t) => {
const { verifyVsceStub, vscePrepareStub, vscePublishStub } = t.context.stubs;
const { publish } = proxyquire('../index.js', {
Expand Down
56 changes: 56 additions & 0 deletions test/publish.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,59 @@ test('publish to OpenVSX', async (t) => {
{ stdio: 'inherit', preferLocal: true, cwd },
]);
});

test('publish to OpenVSX only', async (t) => {
const { execaStub } = t.context.stubs;
const publisher = 'semantic-release-vsce';
const name = 'Semantice Release VSCE';
const publish = proxyquire('../lib/publish', {
execa: execaStub,
'fs-extra': {
readJson: sinon.stub().returns({
publisher,
name,
}),
},
});

const version = '1.0.0';
const packagePath = 'test.vsix';
const token = 'abc123';
sinon.stub(process, 'env').value({
OVSX_PAT: token,
});
const result = await publish(version, packagePath, logger, cwd);

t.deepEqual(result, {
name: 'Open VSX Registry',
url: `https://open-vsx.org/extension/${publisher}/${name}/${version}`,
});
t.true(execaStub.calledOnce);
t.deepEqual(execaStub.getCall(0).args, [
'ovsx',
['publish', '--packagePath', packagePath],
{ stdio: 'inherit', preferLocal: true, cwd },
]);
});

test('errors when neither `VSCE_PAT` nor `OVSX_PAT` set', async (t) => {
const { execaStub } = t.context.stubs;
const publisher = 'semantic-release-vsce';
const name = 'Semantice Release VSCE';
const publish = proxyquire('../lib/publish', {
execa: execaStub,
'fs-extra': {
readJson: sinon.stub().returns({
publisher,
name,
}),
},
});

const version = '1.0.0';
const packagePath = 'test.vsix';
sinon.stub(process, 'env').value({});

const error = await t.throwsAsync(publish(version, packagePath, logger, cwd));
t.deepEqual(error.code, 'ENOPAT');
});
27 changes: 26 additions & 1 deletion test/verify.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const SemanticReleaseError = require('@semantic-release/error');
const test = require('ava');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
Expand Down Expand Up @@ -62,7 +63,7 @@ test('rejects with verify-ovsx-auth', async (t) => {
await t.throwsAsync(() => verify({}, { logger, cwd }));
});

test('is does not verify the auth tokens if publishing is disabled', async (t) => {
test('it does not verify the auth tokens if publishing is disabled', async (t) => {
const stubs = {
verifyPkgStub: sinon.stub().resolves(),
verifyTargetStub: sinon.stub().resolves(),
Expand All @@ -81,3 +82,27 @@ test('is does not verify the auth tokens if publishing is disabled', async (t) =
t.true(stubs.verifyAuthStub.notCalled);
t.true(stubs.verifyOvsxAuthStub.notCalled);
});

test('it should handle `ENOVSCEPAT` error when publishing to OpenVSX is enabled', async (t) => {
const stubs = {
verifyPkgStub: sinon.stub().resolves(),
verifyTargetStub: sinon.stub().resolves(),
verifyAuthStub: sinon
.stub()
.rejects(new SemanticReleaseError('message', 'ENOVSCEPAT')),
verifyOvsxAuthStub: sinon.stub().resolves(),
utilsStub: { isOvsxEnabled: sinon.stub().returns(true) },
};
const verify = proxyquire('../lib/verify', {
'./verify-pkg': stubs.verifyPkgStub,
'./verify-target': stubs.verifyTargetStub,
'./verify-auth': stubs.verifyAuthStub,
'./verify-ovsx-auth': stubs.verifyOvsxAuthStub,
'./utils': stubs.utilsStub,
});

await verify({}, { logger, cwd });

t.deepEqual(stubs.verifyAuthStub.getCall(0).args, [logger, cwd]);
t.deepEqual(stubs.verifyOvsxAuthStub.getCall(0).args, [logger, cwd]);
});