Skip to content

Commit

Permalink
Add clean command
Browse files Browse the repository at this point in the history
  • Loading branch information
megahertz committed Jan 24, 2019
1 parent 7ee7991 commit b71efb0
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 33 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ Install with [npm](https://npmjs.org/package/electron-simple-publisher):
"readme": "The first version"
},
// If you've already set package.json:updater.url you can skip this option:
"updatesJsonUrl": "http://example.com/updates/update.json"
"updatesJsonUrl": "http://example.com/updates/update.json",
// Builds contained these substrings will be ignored when run clean command
"except": [
"prod-v0.5.0"
]
}
```

Expand All @@ -69,6 +73,8 @@ Commands (default is publish):
publish [configFile] [buildId1 Id2 …|all] Publish a new build(s).
replace [configFile] [buildId] Replace the current build.
remove [configFile] [buildId1 Id2 …] Remove one or more builds.
clean [configFile] Remove builds missed in updates.json
-e or --except NAME1,NAME2 NAME1,NAME2 will be preserved
list [configFile] Show builds on a hosting.
BuildId has a following format: [platform]-[arch]-[channel]-v[version]
Expand Down
60 changes: 60 additions & 0 deletions lib/commands/clean.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use strict';

/* eslint-disable no-await-in-loop */

module.exports = clean;
module.exports.NAME = 'clean';
module.exports.filterExceptions = filterExceptions;
module.exports.extractKeysFromUpdatesJson = extractKeysFromUpdatesJson;

/**
* Return promise
* @param {object} options
* @return {Promise}
*/
async function clean(options) {
/** @type {AbstractTransport} */
const transport = options.transport.instance;
const updatesJson = await transport.fetchUpdatesJson();

if (updatesJson.isEmpty) {
console.warn('Can\'t clean because updates.json is not available.');
return [];
}

const list = filterExceptions(await transport.fetchBuildsList(), [
...extractKeysFromUpdatesJson(updatesJson),
...(options.except && options.except.forEach ? options.except : []),
]);

for (const build of list) {
await transport.beforeRemove(build);
await transport.removeBuild(build);
await transport.afterRemove(build);
}

return list;
}

function filterExceptions(list, exceptions) {
return list.filter((build) => {
return !exceptions.some(ex => build.includes(ex));
});
}

function extractKeysFromUpdatesJson(updatesJson) {
return Object.values(updatesJson).reduce((keys, section) => {
if (!section) return keys;

Object.values(section).forEach((str) => {
if (!str.match) return;

const matches = str.match(/(\w+-v\d+\.\d+\.\d+)/);
if (matches && matches[1]) {
keys.push(matches[1]);
}
});

return keys;
}, []);
}
39 changes: 39 additions & 0 deletions lib/commands/clean.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';

const { expect } = require('chai');
const {
extractKeysFromUpdatesJson,
filterExceptions,
} = require('./clean');

describe('Clean command', () => {
it('filterExceptions should return values not existed in preserved', () => {
const values = ['win32-v0.0.1', 'win32-x64-stage-0.1.0', 'test0.0.1'];

expect(filterExceptions(values, ['win32'])).to.deep.equal([
'test0.0.1',
]);

expect(filterExceptions(values, ['0.0.1'])).to.deep.equal([
'win32-x64-stage-0.1.0',
]);
});

it('extractKeysFromUpdatesJson should extract strings like buildId', () => {
const json = {
win32: {
install: 'https://example.com/win32-v0.0.1',
},
'linux-x64-stage': {
prevVersion: 'v1.0.2-beta',
comment: '1.0.2',
install: 'https://example.com/linux-x64-stage-v1.0.2',
},
};

expect(extractKeysFromUpdatesJson(json)).to.deep.equal([
'win32-v0.0.1',
'stage-v1.0.2',
]);
});
});
3 changes: 2 additions & 1 deletion lib/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
const publish = require('./publish');
const replace = require('./replace');
const remove = require('./remove');
const clean = require('./clean');
const list = require('./list');

module.exports = {
publish,
replace,
remove,
list,
NAMES: [publish.NAME, replace.NAME, remove.NAME, list.NAME],
NAMES: [publish.NAME, replace.NAME, remove.NAME, list.NAME, clean.NAME],
};
16 changes: 12 additions & 4 deletions lib/transport/abstract.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class AbstractTransport extends EventEmitter {
/**
* Return an array with all builds stored on a hosting
* @abstract
* @return {Promise<Array<string>>}
* @return {Promise<string[]>}
*/
fetchBuildsList() {
throw new Error('Not implemented');
Expand Down Expand Up @@ -150,14 +150,22 @@ class AbstractTransport extends EventEmitter {
* @return {Promise<object>} data of updates.json
*/
fetchUpdatesJson() {
const empty = {};
Object.defineProperty(empty, 'isEmpty', {
configurable: false,
enumerable: false,
value: true,
writable: false,
});

return new Promise((resolve) => {
http.get(this.getUpdatesJsonUrl(), (err, res) => {
if (err) {
console.warn(
`Could not get updates.json. ${IGNORE_WARNING} `
+ `An error occurred ${err.message}`
);
resolve({});
resolve(empty);
return;
}

Expand All @@ -166,15 +174,15 @@ class AbstractTransport extends EventEmitter {
`Could not get updates.json. ${IGNORE_WARNING} A hosting `
+ `response is:\n${res.statusCode} ${res.body}`
);
resolve({});
resolve(empty);
return;
}

try {
resolve(JSON.parse(res.body));
} catch (e) {
console.warn('Unable to parse updates.json', IGNORE_WARNING, e);
resolve({});
resolve(empty);
}
});
});
Expand Down
12 changes: 12 additions & 0 deletions lib/utils/get-options-from-cli.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

/* eslint-disable padded-blocks */

const minimist = require('minimist');
const commands = require('./../commands/index');

Expand All @@ -15,6 +17,7 @@ function getOptionsFromCli(argv) {
path: 'p',
debug: 'd',
noprogress: 'n',
except: 'e',
},
});

Expand Down Expand Up @@ -61,12 +64,19 @@ function transformCliToOptions(cli) {
if (field.startsWith('field-')) {
// Set field-* to options.fields
options.fields[field.substring(6)] = cli[field];

} else if (field.startsWith('transport-')) {
// Set transport-* to options.transport
options.transport[field.substring(10)] = cli[field];

} else if (field === 'transport') {
// Set transport to options.transport.module
options.transport.module = cli[field];

} else if (field === 'except') {
const ex = cli[field];
options.except = ex && ex.split ? ex.split(',') : [];

} else {
options[field] = cli[field];
}
Expand All @@ -86,6 +96,8 @@ Commands (default is publish):
publish [configFile] [buildId1 Id2 …|all] Publish a new build(s).
replace [configFile] [buildId] Replace the current build.
remove [configFile] [buildId1 Id2 …] Remove one or more builds.
clean [configFile] Remove builds missed in updates.json
-e or --except NAME1,NAME2 NAME1,NAME2 will be preserved
list [configFile] Show builds on a hosting.
BuildId has a following format: [platform]-[arch]-[channel]-v[version]
Expand Down
14 changes: 14 additions & 0 deletions lib/utils/get-options-from-cli.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,20 @@ describe('CLI', () => {
transport: { module: 'github', token: 123 },
});
});

it('should clean with except param', () => {
const options = cmd(
'clean -e win32,linux-x64'
);

expect(options).to.deep.equal({
command: 'clean',
builds: [],
fields: { },
except: ['win32', 'linux-x64'],
transport: {},
});
});
});

function cmd(args) {
Expand Down
73 changes: 47 additions & 26 deletions lib/utils/normalize-options.spec.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
'use strict';

/* eslint-disable padded-blocks */

const { expect } = require('chai');
const mod = require('./normalize-options');


describe('Options module', () => {

it('should apply options from package.json', () => {
const packageJson = {
version: '1.0.1',
Expand Down Expand Up @@ -79,35 +75,60 @@ describe('Options module', () => {
it('should validate remove builds', () => {
const opt1 = {
command: 'remove',
builds: [{
platform: 'win32',
arch: 'x64',
version: '0.0.2',
channel: 'beta',
}, {
platform: 'linux',
arch: 'x64',
version: '0.0.2',
channel: 'beta',
}],
builds: [
{
platform: 'win32',
arch: 'x64',
version: '0.0.2',
channel: 'beta',
},
{
platform: 'linux',
arch: 'x64',
version: '0.0.2',
channel: 'beta',
},
],
};
expect(mod.validateIfRemoveCommand.bind(null, opt1)).to.not.throw(Error);

const opt2 = {
command: 'remove',
builds: [{
platform: 'win32',
arch: 'x64',
version: '0.0',
channel: 'beta',
}, {
platform: 'linux',
arch: 'x64',
version: '0.0.2',
channel: 'beta',
}],
builds: [
{
platform: 'win32',
arch: 'x64',
version: '0.0',
channel: 'beta',
}, {
platform: 'linux',
arch: 'x64',
version: '0.0.2',
channel: 'beta',
},
],
};
expect(mod.validateIfRemoveCommand.bind(null, opt2)).to.throw(Error);
});

it('should apply options from applyConfigJson', () => {
const packageJson = {
transport: {
module: 'github',
},
except: [
'v0.5.0',
],
};

expect(mod.applyConfigJson({}, packageJson)).to.deep.equal({
transport: {
module: 'github',
},
except: [
'v0.5.0',
],
fields: {},
});
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "electron-simple-publisher",
"version": "0.5.0",
"version": "0.5.1",
"description": "Simple way to publish releases for electron-simple-updater",
"main": "index.js",
"scripts": {
Expand Down

0 comments on commit b71efb0

Please sign in to comment.