@hexonet/semantic-release-whmcs 4.0.2 → 4.0.3

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.
@@ -1,105 +1,86 @@
1
1
  const debug = require('debug')('semantic-release:whmcs')
2
- const resolveConfig = require('./resolve-config')
3
2
  const puppet = require('./puppet')
3
+ const path = require('path')
4
4
 
5
5
  /**
6
6
  * A method to publish the module update on whmcs market place
7
7
  */
8
8
  module.exports = async (pluginConfig, context) => {
9
- const {
10
- version,
11
- logger
12
- } = context
9
+ // if (!context.logger) {
10
+ // context.logger = console;
11
+ // }
13
12
 
13
+ const sep = '+++++++++++++++++++++++++++++++++++++++++++++++++++'
14
+ const out = `\n${sep}\n${path.basename(__filename)}\n${sep}\n`
15
+
16
+ const { version } = context
14
17
  if (!version || !version.length) {
15
- debug('deleting product version failed. No input data available.')
16
- logger.error('WHMCS Marketplace deleting product version failed. No input data available.')
18
+ debug(`${out}Deleting product version failed. No input data available.`)
17
19
  return false
18
20
  }
19
21
 
20
- let success = true
21
- const { whmcsLOGIN, whmcsPASSWORD, whmcsPRODUCTID, withHEAD, DEBUG } = resolveConfig(context)
22
- const { gotoOpts, navOpts, selectorOpts, page } = await puppet(withHEAD, DEBUG, logger)
23
-
24
- debug(`WHMCS Marketplace Product ID: ${whmcsPRODUCTID}`)
25
- logger.log(`WHMCS Marketplace Product ID: ${whmcsPRODUCTID}`)
22
+ debug(`${out}Delete Version: ${version}`)
26
23
 
27
- debug(`Delete Version: ${version}`)
28
- logger.log(`Delete Version: ${version}`)
24
+ const püppi = await puppet(context)
25
+ const result = await püppi.login()
26
+ if (!result) {
27
+ return result
28
+ }
29
+ const { page, navOpts, gotoOpts, selectorOpts, productid, urlbase } =
30
+ püppi.config
29
31
 
30
32
  // strip markdown links from notes as not allowed to keep
31
- const wmbase = 'https://marketplace.whmcs.com'
32
- let url = `${wmbase}/user/login`
33
-
34
33
  try {
34
+ // navigate to product administration
35
+ const url = `${urlbase}/product/${productid}/edit#versions`
35
36
  await page.goto(url, gotoOpts)
36
- // do login
37
- const selector = 'div.login-leftcol form button[type="submit"]'
38
- await page.waitForSelector(selector, selectorOpts)
39
- debug('login form loaded at %s', url)
40
- await page.type('#email', whmcsLOGIN)
41
- await page.type('#password', whmcsPASSWORD)
42
- debug('WHMCS Marketplace credentials entered')
43
- const nav = page.waitForNavigation(navOpts)
44
- await page.hover(selector)
45
- await page.click(selector)
46
- await nav
47
- debug('Login form successfully submitted.')
48
- logger.log('WHMCS Marketplace Login Form successfully submitted.')
49
- } catch (error) {
50
- debug('WHMCS Marketplace login failed.', error.message)
51
- logger.error('WHMCS Marketplace login failed.', error.message)
52
- success = false
53
- }
54
- if (success) {
55
- try {
56
- if (!parseInt(whmcsPRODUCTID, 10)) {
57
- return false
58
- }
59
- // open versions tab
60
- url = `${wmbase}/product/${whmcsPRODUCTID}/edit#versions`
61
- await page.goto(url, gotoOpts)
62
- debug('product page loaded at %s', url)
37
+ debug('product page loaded at %s', url)
38
+ // open versions tab (instead of doing it via url)
39
+ // let selector = '#nav-tabs li a[href="#versions"]';
40
+ // await page.waitForSelector(selector, selectorOpts);
41
+ // await page.click(selector);
63
42
 
64
- // delete version
65
- const xpath = `//td[contains(., "Version ${version}")]/following-sibling::td/a[contains(@class, "btn-styled-red")]`
66
- await page.waitForXPath(xpath, selectorOpts)
43
+ // delete version
44
+ // xpath improvements / changes with v16.1.0
45
+ // -> https://github.com/puppeteer/puppeteer/pull/8730
46
+ // https://github.com/puppeteer/puppeteer/blob/d1681ec06b7c3db4b51c20b17b3339f852efbd4d/test/src/queryhandler.spec.ts
47
+ let elements = []
48
+ do {
49
+ const xpath = `xpath/.//td[contains(., "Version ${version}")]/following-sibling::td/a[contains(@class, "btn-styled-red")]`
50
+ await page.waitForSelector(xpath, selectorOpts)
67
51
  debug('XPath found.')
68
- let nav = page.waitForNavigation(navOpts)
69
- /* istanbul ignore next */
70
- const elements = await page.$x(xpath)
71
- debug('Delete Button - click.')
72
- await elements[0].hover()
73
- await elements[0].click()
74
- debug('Delete Button - clicked.')
75
- await nav
76
- debug('Navigation finished.')
77
-
78
- // confirm deletion
79
- const selector = 'div.listing-edit-container form button[type="submit"]'
80
- await page.waitForSelector(selector, selectorOpts)
81
- debug('confirmation form loaded at %s', url)
82
- nav = page.waitForNavigation(navOpts)
83
- await page.hover(selector)
84
- await page.click(selector)
85
- await nav
86
-
87
- debug('deleting product version succeeded.')
88
- logger.log('WHMCS Marketplace deleting product version succeeded.')
89
- success = true
90
- } catch (error) {
91
- debug('deleting product version failed.', error.message)
92
- logger.error('WHMCS Marketplace deleting product version failed.', error.message)
93
- success = false
52
+ const nav = page.waitForNavigation(navOpts)
53
+ elements = await page.$$(xpath)
54
+ if (elements.length) {
55
+ debug('Delete Button - click.')
56
+ await elements[0].hover()
57
+ await elements[0].click()
58
+ debug('Delete Button - clicked.')
59
+ await nav
60
+ debug('Navigation finished.')
61
+ // confirm deletion
62
+ const selector = 'button.btn-styled-red'
63
+ await page.waitForSelector(selector, selectorOpts)
64
+ debug('deletion confirmation button available')
65
+ debug('click confirmation button')
66
+ await page.clickAndNavigate(selector)
67
+ debug('clicked confirmation button')
68
+ await nav
69
+ debug('WHMCS Marketplace deleting product version succeeded.')
70
+ }
71
+ } while (elements.length)
72
+ } catch (error) {
73
+ // while loop and having all versions deleted
74
+ if (!/waiting for selector `.\/\//i.test(error.message)) {
75
+ debug('Deleting product version failed.', error.message)
76
+ await page.browser().close()
77
+ return false
94
78
  }
95
79
  }
96
80
 
97
81
  await page.browser().close()
98
- if (!success) {
99
- return false
100
- }
101
82
  return {
102
83
  name: 'WHMCS Marketplace Product Version',
103
- url: `${wmbase}/product/${whmcsPRODUCTID}`
84
+ url: `${urlbase}/product/${productid}`
104
85
  }
105
86
  }
@@ -9,27 +9,25 @@ const GitHub = require('github-api')
9
9
  * A method to get releases from github repository
10
10
  */
11
11
  module.exports = async (pluginConfig, context) => {
12
- const {
13
- logger
14
- } = context
15
-
16
12
  debug('Getting releases from GitHub')
17
- logger.log('Getting releases from GitHub')
18
13
 
19
- const { githubREPO, githubTOKEN } = resolveConfig(context)
14
+ const { ghrepo, ghtoken } = resolveConfig(context)
20
15
 
21
16
  const gh = new GitHub({
22
- token: githubTOKEN
17
+ token: ghtoken
23
18
  })
24
- if (githubREPO) { // optional by default false
25
- const repo = githubREPO.split('/')
19
+ if (ghrepo) {
20
+ // optional by default false
21
+ const repo = ghrepo.split('/')
26
22
  const githubReleases = await gh.getRepo(repo[0], repo[1]).listReleases()
27
23
  if (githubReleases.status === 200) {
28
- githubReleases.data.forEach(r => logger.log(`Detected GitHub release ${r.name.substring(1)}`))
24
+ githubReleases.data.forEach((r) =>
25
+ debug(`Detected GitHub release ${r.name.substring(1)}`)
26
+ )
29
27
  githubReleases.data.reverse()
30
28
  return githubReleases.data
31
29
  }
32
- logger.log('Failed to get releases from GitHub')
30
+ debug('Failed to get releases from GitHub')
33
31
  }
34
32
  return false
35
33
  }
package/lib/publish.js CHANGED
@@ -1,136 +1,95 @@
1
- const debug = require('debug')('semantic-release:whmcs')
2
- const resolveConfig = require('./resolve-config')
3
- const puppet = require('./puppet')
4
- const setCompatibleVersions = require('./set-compatible-versions')
1
+ const debug = require("debug")("semantic-release:whmcs");
2
+ const puppet = require("./puppet");
3
+ const setCompatibleVersions = require("./set-compatible-versions");
4
+ const path = require("path");
5
5
 
6
6
  /**
7
7
  * A method to publish the module update on whmcs market place
8
8
  */
9
9
  module.exports = async (pluginConfig, context) => {
10
+ const sep = "+++++++++++++++++++++++++++++++++++++++++++++++++++";
11
+ const out = `\n${sep}\n${path.basename(__filename)}\n${sep}\n`;
12
+
10
13
  const {
11
14
  nextRelease: { notes, version, releaseDate },
12
- logger
13
- } = context
14
-
15
+ } = context;
15
16
  if (!notes || !notes.length || !version || !version.length) {
16
- debug('publishing new product version failed. No input data available.')
17
- logger.error(
18
- 'WHMCS Marketplace publishing new product version failed. No input data available.'
19
- )
17
+ debug(
18
+ `${out}Publishing new product version failed. No input data available.`
19
+ );
20
+ return false;
20
21
  }
21
-
22
- let success = true
23
- const { whmcsLOGIN, whmcsPASSWORD, whmcsPRODUCTID, withHEAD, DEBUG } =
24
- resolveConfig(context)
25
- const { gotoOpts, navOpts, selectorOpts, page } = await puppet(withHEAD, DEBUG, logger)
26
-
27
- debug(`WHMCS Marketplace Product ID: ${whmcsPRODUCTID}`)
28
- logger.log(`WHMCS Marketplace Product ID: ${whmcsPRODUCTID}`)
29
-
30
- debug(`Release Version: ${version}`)
31
- logger.log(`Release Version: ${version}`)
32
- debug(`Notes: ${notes}`)
33
- logger.log(`Release Notes: ${notes}`)
34
-
35
22
  // strip markdown links from notes as not allowed to keep (taken from remove-markdown and cleaned up)
36
- const cleanedNotes = notes.replace(/\[([^[\]]*)\]\([^()]*\)/gm, '$1')
37
-
38
- const wmbase = 'https://marketplace.whmcs.com'
39
- let url = `${wmbase}/user/login`
23
+ const cleanedNotes = notes.replace(/\[([^[\]]*)\]\([^()]*\)/gm, "$1");
40
24
 
41
- try {
42
- await page.goto(url, gotoOpts)
43
- // do login
44
- const selector = 'div.login-leftcol form button[type="submit"]'
45
- await page.waitForSelector(selector, selectorOpts)
46
- debug('login form loaded at %s', url)
47
- await page.type('#email', whmcsLOGIN)
48
- await page.type('#password', whmcsPASSWORD)
49
- debug('WHMCS Marketplace credentials entered')
50
- const nav = page.waitForNavigation(navOpts)
51
- await page.hover(selector)
52
- await page.click(selector)
53
- await nav
54
- debug('Login form successfully submitted.')
55
- logger.log('WHMCS Marketplace Login Form successfully submitted.')
56
- } catch (error) {
57
- debug('WHMCS Marketplace login failed.', error.message)
58
- logger.error('WHMCS Marketplace login failed.', error.message)
59
- success = false
25
+ const püppi = await puppet(context);
26
+ const result = await püppi.login();
27
+ if (!result) {
28
+ return result;
60
29
  }
61
- if (success) {
62
- try {
63
- if (!parseInt(whmcsPRODUCTID, 10)) {
64
- return false
65
- }
66
- // add new version
67
- url = `${wmbase}/product/${whmcsPRODUCTID}/versions/new`
68
- await page.goto(url, gotoOpts)
69
- debug('product page loaded at %s', url)
70
- const selector = 'div.listing-edit-container form button[type="submit"]'
71
- await page.waitForSelector(selector, selectorOpts)
72
- debug('product page submit button selector found')
73
- /* istanbul ignore next */
74
- await page.$eval(
75
- '#version',
76
- (el, value) => {
77
- el.value = value
78
- },
79
- version
80
- )
81
- debug('form input for version finished.')
30
+ const { page, gotoOpts, selectorOpts, productid, urlbase } = püppi.config;
82
31
 
83
- // fill input type date with localized string
84
- // https://www.mattzeunert.com/2020/04/01/filling-out-a-date-input-with-puppeteer.html
85
- const date = releaseDate ? new Date(releaseDate) : new Date()
86
- /* istanbul ignore next */
87
- const dateString = await page.evaluate(
88
- (d) =>
89
- new Date(d).toLocaleDateString(navigator.language, {
90
- day: '2-digit',
91
- month: '2-digit',
92
- year: 'numeric'
93
- }),
94
- date.toISOString()
95
- )
96
- await page.type('#released_at', dateString)
32
+ debug(`Release Version: ${version}`);
33
+ debug(`Notes: ${notes}`);
97
34
 
98
- debug('form input for released_at finished.')
99
- /* istanbul ignore next */
100
- await page.$eval(
101
- '#description',
102
- (el, value) => {
103
- el.value = value
104
- },
105
- cleanedNotes
106
- )
107
- debug('form input for description finished.')
108
- const nav = page.waitForNavigation(navOpts)
109
- await page.hover(selector)
110
- await page.click(selector)
111
- await nav
35
+ try {
36
+ // add new version
37
+ const url = `${urlbase}/product/${productid}/versions/new`;
38
+ await page.goto(url, gotoOpts);
39
+ debug("product page loaded at %s", url);
40
+ const selector = 'div.listing-edit-container form button[type="submit"]';
41
+ await page.waitForSelector(selector, selectorOpts);
42
+ debug("product page submit button selector found");
43
+ /* istanbul ignore next */
44
+ await page.$eval(
45
+ "#version",
46
+ (el, value) => {
47
+ el.value = value;
48
+ },
49
+ version
50
+ );
51
+ debug("form input for version finished.");
112
52
 
113
- await setCompatibleVersions(pluginConfig, context)
53
+ // fill input type date with localized string
54
+ // https://www.mattzeunert.com/2020/04/01/filling-out-a-date-input-with-puppeteer.html
55
+ const date = releaseDate ? new Date(releaseDate) : new Date();
56
+ /* istanbul ignore next */
57
+ const dateString = await page.evaluate(
58
+ (d) =>
59
+ new Date(d).toLocaleDateString(navigator.language, {
60
+ day: "2-digit",
61
+ month: "2-digit",
62
+ year: "numeric",
63
+ }),
64
+ date.toISOString()
65
+ );
66
+ await page.enterAndType("#released_at", dateString);
114
67
 
115
- debug('publishing new product version succeeded.')
116
- logger.log('WHMCS Marketplace publishing new product version succeeded.')
117
- success = true
118
- } catch (error) {
119
- debug('publishing new product version failed.', error.message)
120
- logger.error(
121
- 'WHMCS Marketplace publishing new product version failed.',
122
- error.message
123
- )
124
- success = false
125
- }
126
- }
68
+ debug("form input for released_at finished.");
69
+ /* istanbul ignore next */
70
+ await page.$eval(
71
+ "#description",
72
+ (el, value) => {
73
+ el.value = value;
74
+ },
75
+ cleanedNotes
76
+ );
77
+ debug("form input for description finished.");
127
78
 
128
- await page.browser().close()
129
- if (!success) {
130
- return false
79
+ await page.clickAndNavigate(
80
+ 'div.listing-edit-container form button[type="submit"]'
81
+ );
82
+ await setCompatibleVersions(pluginConfig, context);
83
+ } catch (error) {
84
+ debug("Publishing new product version failed.", error.message);
85
+ await page.browser().close();
86
+ return false;
131
87
  }
88
+ debug("Publishing new product version succeeded.");
89
+ await page.browser().close();
90
+
132
91
  return {
133
- name: 'WHMCS Marketplace Product Version',
134
- url: `${wmbase}/product/${whmcsPRODUCTID}`
135
- }
136
- }
92
+ name: "WHMCS Marketplace Product Version",
93
+ url: `${urlbase}/product/${productid}`,
94
+ };
95
+ };
package/lib/puppet.js CHANGED
@@ -1,25 +1,31 @@
1
1
  const puppeteer = require('puppeteer')
2
+ const debug = require('debug')('semantic-release:whmcs')
3
+ const resolveConfig = require('./resolve-config')
2
4
 
3
- module.exports = async (withHEAD, 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
5
+ module.exports = async (context) => {
6
+ const cfg = {
7
+ urlbase: 'https://marketplace.whmcs.com',
8
+ ...resolveConfig(context),
9
+ // logger: logger,
10
+ gotoOpts: {
11
+ waitUntil: ['load', 'domcontentloaded'],
12
+ timeout: 240000
13
+ },
14
+ navOpts: {
15
+ waitUntil: ['networkidle0'],
16
+ timeout: 240000
17
+ },
18
+ selectorOpts: {
19
+ timeout: 10000
20
+ },
21
+ logger: context.logger
17
22
  }
18
23
 
19
24
  const browser = await puppeteer.launch({
20
- headless: !withHEAD,
25
+ headless: cfg.headless === '1',
21
26
  defaultViewport: null, // automatically full-sized
22
27
  args: [
28
+ '--disable-gpu',
23
29
  '--incognito',
24
30
  '--start-maximized',
25
31
  '--no-sandbox',
@@ -27,24 +33,21 @@ module.exports = async (withHEAD, DEBUG, logger) => {
27
33
  '--disable-infobars',
28
34
  '--ignore-certifcate-errors',
29
35
  '--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"'
36
+ '--ignoreHTTPSErrors=true'
32
37
  ]
33
38
  })
39
+ const { logger } = cfg
34
40
  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
- }
41
+ await page.setExtraHTTPHeaders({
42
+ 'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8'
43
43
  })
44
+ await page.setUserAgent(
45
+ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
46
+ )
44
47
  await page.setJavaScriptEnabled(true)
45
48
 
46
- if (DEBUG) {
47
- page.on('console', msg => {
49
+ if (cfg.debug) {
50
+ page.on('console', (msg) => {
48
51
  for (let i = 0; i < msg.args().length; i++) {
49
52
  logger.log(`${i}: ${msg.args()[i]}`)
50
53
  }
@@ -52,11 +55,59 @@ module.exports = async (withHEAD, DEBUG, logger) => {
52
55
  }
53
56
 
54
57
  page.clickAndNavigate = async (selector) => {
58
+ const { page, navOpts } = this.config
55
59
  const nav = page.waitForNavigation(navOpts)
56
60
  await page.hover(selector)
57
61
  await page.click(selector)
58
62
  await nav
59
63
  }
60
64
 
61
- return { page, gotoOpts, navOpts, selectorOpts }
65
+ page.enterAndType = async (selector, value) => {
66
+ const { page, selectorOpts } = this.config
67
+ await page.waitForSelector(selector, selectorOpts)
68
+ await page.type(selector, value)
69
+ }
70
+
71
+ this.login = async () => {
72
+ const { page, login, password, productid, gotoOpts, urlbase } = this.config
73
+ const selector = 'div.login-leftcol form button[type="submit"]'
74
+ // do login
75
+ try {
76
+ await page.goto(`${urlbase}/user/login`, gotoOpts)
77
+ debug('login form loaded at %s', `${urlbase}/user/login`)
78
+ await page.enterAndType('#email', login)
79
+ await page.enterAndType('#password', password)
80
+ debug('WHMCS Marketplace credentials entered')
81
+ await page.clickAndNavigate(selector)
82
+ debug('WHMCS Marketplace login form submitted.')
83
+ } catch (error) {
84
+ debug(
85
+ 'WHMCS Marketplace login failed or Product ID missing',
86
+ error.message
87
+ )
88
+ await page.browser().close()
89
+ return false
90
+ }
91
+ debug('WHMCS Marketplace login succeeded.')
92
+
93
+ // access MP Product ID
94
+ let tmp = productid
95
+ if (!tmp || !/^[0-9]+$/.test(productid) || !parseInt(productid, 10)) {
96
+ debug('No or invalid WHMCS Marketplace Product ID provided.')
97
+ await page.browser().close()
98
+ return false
99
+ }
100
+
101
+ tmp = tmp.replace(/(.)/g, '$&\u200E')
102
+ debug(`WHMCS Marketplace Product ID: ${tmp}`)
103
+
104
+ return true
105
+ }
106
+
107
+ this.config = {
108
+ ...cfg,
109
+ page
110
+ }
111
+
112
+ return this
62
113
  }
@@ -1,10 +1,11 @@
1
1
  module.exports = ({ env }) => ({
2
- whmcsLOGIN: env.WHMCSMP_LOGIN || false,
3
- whmcsPASSWORD: env.WHMCSMP_PASSWORD || false,
4
- whmcsPRODUCTID: env.WHMCSMP_PRODUCTID || false,
5
- whmcsMINVERSION: env.WHMCSMP_MINVERSION || '7.10',
6
- githubTOKEN: env.GH_TOKEN || env.GITHUB_TOKEN || false,
7
- githubREPO: env.GH_REPO || env.GITHUB_REPO || false,
8
- withHEAD: env.WITH_HEAD || false,
9
- DEBUG: (env.DEBUG && /^semantic-release:(\*|whmcs)$/.test(env.DEBUG)) || false
2
+ login: env.WHMCSMP_LOGIN || false,
3
+ password: env.WHMCSMP_PASSWORD || false,
4
+ productid: env.WHMCSMP_PRODUCTID || false,
5
+ minversion: env.WHMCSMP_MINVERSION || '7.10',
6
+ ghtoken: env.GH_TOKEN || env.GITHUB_TOKEN || false,
7
+ ghrepo: env.GH_REPO || env.GITHUB_REPO || false,
8
+ headless: env.PUPPETEER_HEADLESS || '1',
9
+ debug:
10
+ (env.DEBUG && /^semantic-release:(\*|whmcs)$/.test(env.DEBUG)) || false
10
11
  })