@hexonet/semantic-release-whmcs 3.0.2 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. package/.eslintrc.js +13 -11
  2. package/.github/workflows/pull-request.yml +47 -0
  3. package/.github/workflows/push.yml +76 -0
  4. package/.releaserc.json +20 -23
  5. package/HISTORY.md +30 -0
  6. package/README.md +5 -5
  7. package/coverage/base.css +224 -0
  8. package/coverage/block-navigation.js +87 -0
  9. package/coverage/coverage-final.json +11 -0
  10. package/coverage/favicon.png +0 -0
  11. package/coverage/index.html +131 -0
  12. package/coverage/lib/definitions/errors.js.html +136 -0
  13. package/coverage/lib/definitions/index.html +116 -0
  14. package/coverage/lib/delete-marketplace-version.js.html +400 -0
  15. package/coverage/lib/get-error.js.html +106 -0
  16. package/coverage/lib/get-github-releases.js.html +190 -0
  17. package/coverage/lib/index.html +236 -0
  18. package/coverage/lib/publish.js.html +490 -0
  19. package/coverage/lib/puppet.js.html +271 -0
  20. package/coverage/lib/resolve-config.js.html +112 -0
  21. package/coverage/lib/scrape-marketplace-versions.js.html +322 -0
  22. package/coverage/lib/set-compatible-versions.js.html +376 -0
  23. package/coverage/lib/verify.js.html +145 -0
  24. package/coverage/prettify.css +1 -0
  25. package/coverage/prettify.js +2 -0
  26. package/coverage/sort-arrow-sprite.png +0 -0
  27. package/coverage/sorter.js +196 -0
  28. package/index.js +11 -9
  29. package/lib/definitions/errors.js +6 -3
  30. package/lib/delete-marketplace-version.js +7 -36
  31. package/lib/get-error.js +1 -1
  32. package/lib/get-github-releases.js +10 -8
  33. package/lib/publish.js +97 -105
  34. package/lib/puppet.js +62 -0
  35. package/lib/resolve-config.js +2 -2
  36. package/lib/scrape-marketplace-versions.js +5 -34
  37. package/lib/set-compatible-versions.js +6 -34
  38. package/package.json +20 -21
  39. package/whmcs.js +1 -2
  40. package/.github/workflows/release.yml +0 -56
  41. package/test/publish.test.js +0 -78
  42. package/test/verify.test.js +0 -54
package/lib/puppet.js ADDED
@@ -0,0 +1,62 @@
1
+ const puppeteer = require('puppeteer')
2
+
3
+ module.exports = async (DEBUG, logger) => {
4
+ if (!logger) {
5
+ logger = console
6
+ }
7
+ const gotoOpts = {
8
+ waitUntil: ['load', 'domcontentloaded'],
9
+ timeout: 240000
10
+ }
11
+ const navOpts = {
12
+ waitUntil: 'networkidle0',
13
+ timeout: 240000
14
+ }
15
+ const selectorOpts = {
16
+ timeout: 10000
17
+ }
18
+
19
+ const browser = await puppeteer.launch({
20
+ headless: !DEBUG,
21
+ defaultViewport: null, // automatically full-sized
22
+ args: [
23
+ '--incognito',
24
+ '--start-maximized',
25
+ '--no-sandbox',
26
+ '--disable-setuid-sandbox',
27
+ '--disable-infobars',
28
+ '--ignore-certifcate-errors',
29
+ '--ignore-certifcate-errors-spki-list',
30
+ '--ignoreHTTPSErrors=true',
31
+ '--user-agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"'
32
+ ]
33
+ })
34
+ const [page] = await browser.pages()
35
+
36
+ await page.setRequestInterception(true)
37
+ page.on('request', request => {
38
+ if (/^(image|stylesheet|other)$/i.test(request.resourceType())) {
39
+ request.abort()
40
+ } else {
41
+ request.continue()
42
+ }
43
+ })
44
+ await page.setJavaScriptEnabled(true)
45
+
46
+ if (DEBUG) {
47
+ page.on('console', msg => {
48
+ for (let i = 0; i < msg.args().length; i++) {
49
+ logger.log(`${i}: ${msg.args()[i]}`)
50
+ }
51
+ })
52
+ }
53
+
54
+ page.clickAndNavigate = async (selector) => {
55
+ const nav = page.waitForNavigation(navOpts)
56
+ await page.hover(selector)
57
+ await page.click(selector)
58
+ await nav
59
+ }
60
+
61
+ return { page, gotoOpts, navOpts, selectorOpts }
62
+ }
@@ -3,7 +3,7 @@ module.exports = ({ env }) => ({
3
3
  whmcsPASSWORD: env.WHMCSMP_PASSWORD || false,
4
4
  whmcsPRODUCTID: env.WHMCSMP_PRODUCTID || false,
5
5
  whmcsMINVERSION: env.WHMCSMP_MINVERSION || '7.10',
6
- githubTOKEN: env.GH_TOKEN || false,
7
- githubREPO: env.GH_REPO || false,
6
+ githubTOKEN: env.GH_TOKEN || env.GITHUB_TOKEN || false,
7
+ githubREPO: env.GH_REPO || env.GITHUB_REPO || false,
8
8
  DEBUG: (env.DEBUG && /^semantic-release:(\*|whmcs)$/.test(env.DEBUG)) || false
9
9
  })
@@ -1,12 +1,6 @@
1
1
  const debug = require('debug')('semantic-release:whmcs')
2
2
  const resolveConfig = require('./resolve-config')
3
- const puppeteer = require('puppeteer')
4
- const gotoOpts = {
5
- waitUntil: ['load', 'domcontentloaded'],
6
- timeout: 240000
7
- }
8
- const navOpts = { waitUntil: 'networkidle0', timeout: 240000 }
9
- const selectorOpts = { timeout: 240000 }
3
+ const puppet = require('./puppet')
10
4
 
11
5
  /**
12
6
  * A method to publish the module update on whmcs market place
@@ -18,40 +12,15 @@ module.exports = async (pluginConfig, context) => {
18
12
 
19
13
  let success = true
20
14
  const { whmcsLOGIN, whmcsPASSWORD, whmcsPRODUCTID, DEBUG } = resolveConfig(context)
15
+ const { gotoOpts, navOpts, selectorOpts, page } = await puppet(DEBUG, logger)
21
16
 
22
17
  debug(`WHMCS Marketplace Product ID: ${whmcsPRODUCTID}`)
23
18
  logger.log(`WHMCS Marketplace Product ID: ${whmcsPRODUCTID}`)
24
19
 
25
20
  const wmbase = 'https://marketplace.whmcs.com'
26
21
  let url = `${wmbase}/user/login`
27
- const browser = await puppeteer.launch({
28
- headless: !DEBUG,
29
- defaultViewport: null, // automatically full-sized
30
- args: [
31
- '--incognito',
32
- '--start-maximized',
33
- '--no-sandbox',
34
- '--disable-setuid-sandbox',
35
- '--disable-infobars',
36
- '--ignore-certifcate-errors',
37
- '--ignore-certifcate-errors-spki-list',
38
- '--ignoreHTTPSErrors=true',
39
- '--user-agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"'
40
- ]
41
- })
42
- const [page] = await browser.pages()
43
22
 
44
23
  try {
45
- await page.setRequestInterception(true)
46
- page.on('request', request => {
47
- if (/^(image|stylesheet|other)$/i.test(request.resourceType())) {
48
- request.abort()
49
- } else {
50
- request.continue()
51
- }
52
- })
53
-
54
- await page.setJavaScriptEnabled(true)
55
24
  await page.goto(url, gotoOpts)
56
25
  // do login
57
26
  const selector = 'div.login-leftcol form button[type="submit"]'
@@ -61,6 +30,7 @@ module.exports = async (pluginConfig, context) => {
61
30
  await page.type('#password', whmcsPASSWORD)
62
31
  debug('WHMCS Marketplace credentials entered')
63
32
  const nav = page.waitForNavigation(navOpts)
33
+ await page.hover(selector)
64
34
  await page.click(selector)
65
35
  await nav
66
36
  debug('Login form successfully submitted.')
@@ -84,6 +54,7 @@ module.exports = async (pluginConfig, context) => {
84
54
  const selector = 'div#versions tr strong'
85
55
  await page.waitForSelector(selector, selectorOpts)
86
56
  debug('product version table found')
57
+ /* istanbul ignore next */
87
58
  marketplaceVersions = await page.$$eval('div#versions tr td strong', tds => tds.map((td) => {
88
59
  return td.innerText.substring(8)
89
60
  }))
@@ -100,7 +71,7 @@ module.exports = async (pluginConfig, context) => {
100
71
  }
101
72
  }
102
73
 
103
- await browser.close()
74
+ await page.browser().close()
104
75
  if (!success) {
105
76
  return false
106
77
  }
@@ -1,12 +1,6 @@
1
1
  const debug = require('debug')('semantic-release:whmcs')
2
2
  const resolveConfig = require('./resolve-config')
3
- const puppeteer = require('puppeteer')
4
- const gotoOpts = {
5
- waitUntil: ['load', 'domcontentloaded'],
6
- timeout: 240000
7
- }
8
- const navOpts = { waitUntil: 'networkidle0', timeout: 240000 }
9
- const selectorOpts = { timeout: 240000 }
3
+ const puppet = require('./puppet')
10
4
 
11
5
  /**
12
6
  * A method to publish the module update on whmcs market place
@@ -18,40 +12,15 @@ module.exports = async (pluginConfig, context) => {
18
12
 
19
13
  let success = true
20
14
  const { whmcsLOGIN, whmcsPASSWORD, whmcsPRODUCTID, whmcsMINVERSION, DEBUG } = resolveConfig(context)
15
+ const { gotoOpts, navOpts, selectorOpts, page } = await puppet(DEBUG, logger)
21
16
 
22
17
  debug(`WHMCS Marketplace Product ID: ${whmcsPRODUCTID}`)
23
18
  logger.log(`WHMCS Marketplace Product ID: ${whmcsPRODUCTID}`)
24
19
 
25
20
  const wmbase = 'https://marketplace.whmcs.com'
26
21
  let url = `${wmbase}/user/login`
27
- const browser = await puppeteer.launch({
28
- headless: !DEBUG,
29
- defaultViewport: null, // automatically full-sized
30
- args: [
31
- '--incognito',
32
- '--start-maximized',
33
- '--no-sandbox',
34
- '--disable-setuid-sandbox',
35
- '--disable-infobars',
36
- '--ignore-certifcate-errors',
37
- '--ignore-certifcate-errors-spki-list',
38
- '--ignoreHTTPSErrors=true',
39
- '--user-agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"'
40
- ]
41
- })
42
- const [page] = await browser.pages()
43
22
 
44
23
  try {
45
- await page.setRequestInterception(true)
46
- page.on('request', request => {
47
- if (/^(image|stylesheet|other)$/i.test(request.resourceType())) {
48
- request.abort()
49
- } else {
50
- request.continue()
51
- }
52
- })
53
-
54
- await page.setJavaScriptEnabled(true)
55
24
  await page.goto(url, gotoOpts)
56
25
  // do login
57
26
  const selector = 'div.login-leftcol form button[type="submit"]'
@@ -61,6 +30,7 @@ module.exports = async (pluginConfig, context) => {
61
30
  await page.type('#password', whmcsPASSWORD)
62
31
  debug('WHMCS Marketplace credentials entered')
63
32
  const nav = page.waitForNavigation(navOpts)
33
+ await page.hover(selector)
64
34
  await page.click(selector)
65
35
  await nav
66
36
  debug('Login form successfully submitted.')
@@ -89,6 +59,7 @@ module.exports = async (pluginConfig, context) => {
89
59
  debug('compatibility version table found')
90
60
 
91
61
  logger.log(`Minimum required WHMCS version: ${whmcsMINVERSION}`)
62
+ /* istanbul ignore next */
92
63
  await page.$$eval(selector, (checkboxes, whmcsMINVERSION) => checkboxes.forEach(function (c, i) {
93
64
  const checkParts = c.className.split('-')[0].split('_')
94
65
  const minParts = whmcsMINVERSION.split('.')
@@ -107,6 +78,7 @@ module.exports = async (pluginConfig, context) => {
107
78
  c.checked = check
108
79
  }), whmcsMINVERSION)
109
80
  const submitNav = page.waitForNavigation(navOpts)
81
+ await page.hover(submitSelector)
110
82
  await page.click(submitSelector)
111
83
  await submitNav
112
84
 
@@ -120,6 +92,6 @@ module.exports = async (pluginConfig, context) => {
120
92
  }
121
93
  }
122
94
 
123
- await browser.close()
95
+ await page.browser().close()
124
96
  return success
125
97
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hexonet/semantic-release-whmcs",
3
3
  "description": "`semantic-release` plugin for auto-publishing on WHMCS marketplace",
4
- "version": "3.0.2",
4
+ "version": "3.1.2",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -13,8 +13,8 @@
13
13
  "main": "index.js",
14
14
  "license": "MIT",
15
15
  "engines": {
16
- "node": ">=10.18.0",
17
- "npm": ">=6.14.8"
16
+ "node": ">=16.15.1",
17
+ "npm": ">=8.11.1"
18
18
  },
19
19
  "homepage": "https://github.com/hexonet/semantic-release-whmcs#readme",
20
20
  "repository": "github:hexonet/semantic-release-whmcs",
@@ -57,29 +57,28 @@
57
57
  "semantic-release": "semantic-release"
58
58
  },
59
59
  "devDependencies": {
60
- "@octokit/core": "^3.2.0",
61
- "@semantic-release/changelog": "^5.0.1",
62
- "@semantic-release/exec": "^5.0.0",
63
- "@semantic-release/git": "^9.0.0",
64
- "ava": "^3.13.0",
65
- "debug": "^4.2.0",
66
- "eslint": "^7.12.1",
67
- "eslint-config-standard": "^16.0.0",
68
- "eslint-plugin-import": "^2.22.1",
69
- "eslint-plugin-json": "^2.1.2",
70
- "eslint-plugin-markdown": "^1.0.2",
71
- "eslint-plugin-node": "^11.1.0",
72
- "eslint-plugin-promise": "^4.2.1",
60
+ "@semantic-release/changelog": "^6.0.1",
61
+ "@semantic-release/exec": "^6.0.2",
62
+ "@semantic-release/git": "^10.0.1",
63
+ "ava": "^4.0.0",
64
+ "eslint": "^8.0.1",
65
+ "eslint-config-standard": "^17.0.0",
66
+ "eslint-plugin-import": "^2.25.2",
67
+ "eslint-plugin-json": "^3.0.0",
68
+ "eslint-plugin-markdown": "^2.1.0",
69
+ "eslint-plugin-n": "^15.2.0",
70
+ "eslint-plugin-promise": "^6.0.0",
73
71
  "esm": "^3.2.25",
74
72
  "nyc": "^15.1.0",
75
- "semantic-release": "^17.2.1"
73
+ "semantic-release": "^18.0.1",
74
+ "stream-buffers": "^3.0.2"
76
75
  },
77
76
  "dependencies": {
78
- "@semantic-release/error": "^2.2.0",
77
+ "@semantic-release/error": "^3.0.0",
79
78
  "aggregate-error": "^3.1.0",
79
+ "debug": "^4.3.4",
80
80
  "github-api": "^3.4.0",
81
- "puppeteer": "^9.0.0",
82
- "sinon": "^9.2.1",
83
- "yargs": "^16.2.0"
81
+ "puppeteer": "^14.0.0",
82
+ "yargs": "^17.0.1"
84
83
  }
85
84
  }
package/whmcs.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
 
3
2
  const { publish, syncVersions, delVersion, updateCompatibility } = require('./index.js')
4
3
 
@@ -7,7 +6,7 @@ const context = {
7
6
  logger: new class {
8
7
  log (msg) { console.log(msg) }
9
8
  info (msg) { console.log(msg) }
10
- error (msg, details) { console.log(msg + ' ' + details) }
9
+ error (msg, details) { console.error(msg + ' ' + details) }
11
10
  }()
12
11
  }
13
12
 
@@ -1,56 +0,0 @@
1
- name: Release
2
- on: [push, pull_request]
3
-
4
- jobs:
5
- test:
6
- name: Test @ NodeJS - x86 - ubuntu-latest
7
- runs-on: ubuntu-latest
8
- strategy:
9
- matrix:
10
- node-version: [14.x]
11
- if: github.event_name == 'pull_request' || (github.event_name == 'push' && contains(toJson(github.event.commits), '[skip ci]') == false)
12
- steps:
13
- - name: Checkout
14
- uses: actions/checkout@v2
15
- - name: Setup NodeJS ${{ matrix.node-version }}
16
- uses: actions/setup-node@v2
17
- with:
18
- node-version: ${{ matrix.node-version }}
19
- - name: Install dependencies
20
- run: |
21
- npm prune
22
- npm i
23
- - name: Linting
24
- run: npm run lint
25
- - name: Tests
26
- env:
27
- GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
28
- GITHUB_REPO: ${{ secrets.GH_REPO }}
29
- WHMCSMP_LOGIN: ${{ secrets.WHMCSMP_LOGIN }}
30
- WHMCSMP_PASSWORD: ${{ secrets.WHMCSMP_PASSWORD }}
31
- WHMCSMP_PRODUCTID: ${{ secrets.WHMCSMP_PRODUCTID_ISPAPI_SSL }}
32
- WHMCSMP_MINVERSION: ${{ secrets.WHMCSMP_MINVERSION }}
33
- run: npm run test
34
- release:
35
- name: Release @ NodeJS LTS - x86 - ubuntu-latest
36
- if: github.ref == 'refs/heads/master' && github.event_name == 'push'
37
- runs-on: ubuntu-latest
38
- needs: "test"
39
- steps:
40
- - name: Checkout
41
- uses: actions/checkout@v2
42
- with:
43
- fetch-depth: 0
44
- persist-credentials: false
45
- - name: Setup NodeJS LTS
46
- uses: actions/setup-node@v2
47
- - name: Install dependencies
48
- run: |
49
- npm prune
50
- npm i
51
- - name: Release
52
- env:
53
- GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
54
- GITHUB_REPO: ${{ secrets.GH_REPO }}
55
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
56
- run: npx semantic-release
@@ -1,78 +0,0 @@
1
- import test from 'ava'
2
- import { stub } from 'sinon'
3
- const publish = require('../lib/publish')
4
- const deleteVersion = require('../lib/delete-marketplace-version')
5
-
6
- test.beforeEach(t => {
7
- // Mock logger
8
- t.context.log = stub()
9
- t.context.error = stub()
10
- t.context.logger = { log: t.context.log, error: t.context.error }
11
- })
12
-
13
- test.serial('Verify publishing [login fails]', async t => {
14
- const context = {
15
- env: {
16
- WHMCSMP_LOGIN: 'anonymous@hexonet.net',
17
- WHMCSMP_PASSWORD: '1234567890',
18
- WHMCSMP_PRODUCTID: '0'
19
- },
20
- logger: t.context.logger,
21
- nextRelease: {
22
- version: 'v6.6.6',
23
- notes: '# something changed\n\ntwice.'
24
- }
25
- }
26
- const result = await publish({}, context)
27
- t.is(result, false)
28
- })
29
-
30
- test.serial('Verify publishing [no product id]', async t => {
31
- const env = {
32
- WHMCSMP_LOGIN: process.env.WHMCSMP_LOGIN,
33
- WHMCSMP_PASSWORD: process.env.WHMCSMP_PASSWORD
34
- }
35
- t.truthy(env.WHMCSMP_LOGIN)
36
- t.truthy(env.WHMCSMP_PASSWORD)
37
- const context = {
38
- env,
39
- logger: t.context.logger,
40
- nextRelease: {
41
- version: 'v6.6.6',
42
- notes: '# something changed\n\ntwice.'
43
- }
44
- }
45
- const result = await publish({}, context)
46
- t.is(result, false)
47
- })
48
-
49
- test.skip('Verify publishing [all fine]', async t => {
50
- const env = process.env
51
- t.truthy(env.WHMCSMP_LOGIN)
52
- t.truthy(env.WHMCSMP_PASSWORD)
53
- t.truthy(env.WHMCSMP_PRODUCTID)
54
- t.truthy(env.WHMCSMP_MINVERSION)
55
- const context = {
56
- env,
57
- logger: t.context.logger,
58
- nextRelease: {
59
- version: '0.0.1',
60
- notes: '# something changed\n\ntwice\n\n[link test](https://github.com/papakai) [link test2](https://github.com/papakai)\n\ndone.'
61
- }
62
- }
63
- const result = await publish({}, context)
64
- t.assert(result !== false)
65
-
66
- // wait 5s between adding and deleting
67
- await new Promise((resolve) => {
68
- setTimeout(resolve, 5000)
69
- })
70
-
71
- const delContext = {
72
- env,
73
- logger: t.context.logger,
74
- version: context.nextRelease.version
75
- }
76
- const del = await deleteVersion({}, delContext)
77
- t.assert(del !== false)
78
- })
@@ -1,54 +0,0 @@
1
- import test from 'ava'
2
- import { stub } from 'sinon'
3
- const verify = require('../lib/verify')
4
- /* eslint camelcase: ["error", {properties: "never"}] */
5
-
6
- test.beforeEach(t => {
7
- // Mock logger
8
- t.context.log = stub()
9
- t.context.error = stub()
10
- t.context.logger = { log: t.context.log, error: t.context.error }
11
- })
12
-
13
- test.serial('Verify credentials [no login]', async t => {
14
- const env = {}
15
- const [error] = await t.throwsAsync(
16
- verify({}, { env, logger: t.context.logger })
17
- )
18
- t.is(error.name, 'SemanticReleaseError')
19
- t.is(error.code, 'EWHMCSNOCREDENTIALS')
20
- })
21
-
22
- test.serial('Verify credentials [no password]', async t => {
23
- const env = { WHMCSMP_LOGIN: 'mylogin' }
24
- const [error] = await t.throwsAsync(
25
- verify({}, { env, logger: t.context.logger })
26
- )
27
- t.is(error.name, 'SemanticReleaseError')
28
- t.is(error.code, 'EWHMCSNOCREDENTIALS')
29
- })
30
-
31
- test.serial('Verify product id [no product id]', async t => {
32
- const env = { WHMCSMP_LOGIN: 'mylogin', WHMCSMP_PASSWORD: 'mypassword' }
33
- const [error] = await t.throwsAsync(
34
- verify({}, { env, logger: t.context.logger })
35
- )
36
- t.is(error.name, 'SemanticReleaseError')
37
- t.is(error.code, 'EWHMCSNOPRODUCTID')
38
- })
39
-
40
- test.serial('Verify product id [invalid product id]', async t => {
41
- const env = { WHMCSMP_LOGIN: 'mylogin', WHMCSMP_PASSWORD: 'mypassword', WHMCSMP_PRODUCTID: 'myproductid' }
42
- const [error] = await t.throwsAsync(
43
- verify({}, { env, logger: t.context.logger })
44
- )
45
- t.is(error.name, 'SemanticReleaseError')
46
- t.is(error.code, 'EWHMCSINVALIDPRODUCTID')
47
- })
48
-
49
- test.serial('Verify data [all fine]', async t => {
50
- const env = { WHMCSMP_LOGIN: 'mylogin', WHMCSMP_PASSWORD: 'mypassword', WHMCSMP_PRODUCTID: '123456' }
51
- await t.notThrowsAsync(
52
- verify({}, { env, logger: t.context.logger })
53
- )
54
- })