@hexonet/semantic-release-whmcs 3.0.2 → 3.1.2

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.
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
- })