@hexonet/semantic-release-whmcs 5.2.8 → 5.2.10

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.
package/.env.example ADDED
@@ -0,0 +1,15 @@
1
+ # Copy this file to .env and fill in real values.
2
+ # Never commit your .env (it is gitignored).
3
+
4
+ WHMCSMP_PRODUCTID=product id
5
+ WHMCSMP_LOGIN=your.email@example.com
6
+ WHMCSMP_PASSWORD=your-password
7
+
8
+ # Recommended for CI/headless environments
9
+ PUPPETEER_HEADLESS=1
10
+
11
+ # Optional: more logs
12
+ # DEBUG=semantic-release:whmcs
13
+
14
+ # Optional: stable cache/workdir in Docker
15
+ # PROJECT_WORKDIR=/usr/share/rtldev-middleware-whmcs-src
package/HISTORY.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## [5.2.10](https://github.com/centralnicgroup-opensource/rtldev-middleware-semantic-release-whmcs/compare/v5.2.9...v5.2.10) (2026-02-16)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **deps:** bump puppeteer from 24.37.2 to 24.37.3 ([171b2e2](https://github.com/centralnicgroup-opensource/rtldev-middleware-semantic-release-whmcs/commit/171b2e262aa53093347265249ddcbc893f606541))
7
+
8
+ ## [5.2.9](https://github.com/centralnicgroup-opensource/rtldev-middleware-semantic-release-whmcs/compare/v5.2.8...v5.2.9) (2026-02-06)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **prepare.js:** update OS dependency installation process for Puppeteer on Debian/Ubuntu ([f52c709](https://github.com/centralnicgroup-opensource/rtldev-middleware-semantic-release-whmcs/commit/f52c709a8ec1d7eff02009e83afd9235083a0509))
14
+ * **prepare/puppet:** enhance prepare.js and puppet.js for improved configuration handling ([450b0e0](https://github.com/centralnicgroup-opensource/rtldev-middleware-semantic-release-whmcs/commit/450b0e0a088afc45286c1cd6ed49b8d325d63c55))
15
+
1
16
  ## [5.2.8](https://github.com/centralnicgroup-opensource/rtldev-middleware-semantic-release-whmcs/compare/v5.2.7...v5.2.8) (2026-02-06)
2
17
 
3
18
 
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  | Step | Description |
13
13
  | ---- | ----------- |
14
- | `prepare` | Install OS dependencies required by Puppeteer and Chromium/Chrome (Debian/Ubuntu only) if `osDepsCommand` is configured. |
14
+ | `prepare` | Install OS dependencies required by Puppeteer and Chromium/Chrome (Debian/Ubuntu only). Can be customized via `osDepsCommand`. |
15
15
  | `verifyConditions` | Verify the presence and the validity of the authentication credentials (set via [environment variables](#environment-variables)) and the product id option configuration. |
16
16
  | `publish` | Publish product/module version to [WHMCS Marketplace](https://marketplace.whmcs.com) including changelog notes. |
17
17
 
@@ -28,7 +28,7 @@ FYI: This module is ESM ready!
28
28
  ### Requirements
29
29
 
30
30
  * Installed nodejs/npm. We suggest using [nvm](https://github.com/creationix/nvm).
31
- * **OS Dependencies**: On Debian/Ubuntu systems, the `prepare` step can run a custom command (e.g. `puppeteer browsers install chrome --install-deps`) to install required packages on demand. This is configured via `osDepsCommand`.
31
+ * **OS Dependencies**: On Debian/Ubuntu systems, the `prepare` step installs Chrome (for testing) + required OS packages using `pnpm dlx puppeteer browsers install chrome --install-deps` (falls back to `npx` if `pnpm` is unavailable). You can override the command via `osDepsCommand`.
32
32
  * Using [semantic-release](https://github.com/semantic-release/semantic-release) in your CI/CD process
33
33
 
34
34
  ### Install
@@ -39,7 +39,7 @@ FYI: This module is ESM ready!
39
39
 
40
40
  ### Configuration
41
41
 
42
- The plugin can be loaded in the [**semantic-release** configuration file](https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#configuration). The `prepare` step handles OS dependencies if `osDepsCommand` is provided (requires `sudo` access on Debian/Ubuntu for system packages).
42
+ The plugin can be loaded in the [**semantic-release** configuration file](https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#configuration). The `prepare` step handles OS dependencies on Debian/Ubuntu (requires `sudo` access for system packages).
43
43
 
44
44
  ```json
45
45
  {
@@ -92,6 +92,8 @@ That said, before you can use this module for publishing new product/module vers
92
92
  | `GH_TOKEN` | **Optional.** GitHub API authentication token to use for syncing versions. |
93
93
  | `GH_REPO` | **Optional.** GitHub repository name (format: organization/repository) to use for syncing versions. |
94
94
  | `useCookieExtension` | **Optional.** Use cookies extension when puppeteer is running to avoid cookie banner disruptions. |
95
+ | `WHMCS_OS_DEPS_ALWAYS` | **Optional.** Force OS dependency installation in `prepare` even if there is no new release version. Useful for CI experiments. Values: `1` / `true`. |
96
+ | `SKIP_OS_DEPS` | **Optional.** Skip OS dependency installation in `prepare`. Values: `1` / `true` (also supported: `skipOsDeps`). |
95
97
 
96
98
  ### Options
97
99
 
package/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ // Set Puppeteer cache dir early, before any puppeteer imports
2
+ import path from "path";
3
+ const workDir = process.env.PROJECT_WORKDIR || process.cwd();
4
+ if (!process.env.PUPPETEER_CACHE_DIR) {
5
+ process.env.PUPPETEER_CACHE_DIR = path.join(workDir, ".cache", "puppeteer");
6
+ }
7
+
1
8
  import verifyWHMCS from "./lib/verify.js";
2
9
  import prepareWHMCS from "./lib/prepare.js";
3
10
  import publishWHMCS from "./lib/publish.js";
package/lib/prepare.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { spawn } from "child_process";
2
2
  import debugConfig from "debug";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import resolveConfig from "./resolve-config.js";
3
6
 
4
7
  const debug = debugConfig("semantic-release:whmcs");
5
8
 
@@ -23,6 +26,26 @@ export function __resetSpawnImplementation() {
23
26
  spawnImplementation = spawn;
24
27
  }
25
28
 
29
+ function resolveCacheDir(context) {
30
+ if (process.env.PUPPETEER_CACHE_DIR) return process.env.PUPPETEER_CACHE_DIR;
31
+ const workDir = process.env.PROJECT_WORKDIR || context?.cwd || process.cwd();
32
+ return path.join(workDir, ".cache", "puppeteer");
33
+ }
34
+
35
+ function resolveInstalledChromeExecutable(cacheDir) {
36
+ try {
37
+ const chromeDir = path.join(cacheDir, "chrome");
38
+ if (!fs.existsSync(chromeDir)) return null;
39
+ const versions = fs.readdirSync(chromeDir).filter(Boolean).sort();
40
+ if (versions.length === 0) return null;
41
+ const latest = versions[versions.length - 1];
42
+ const candidate = path.join(chromeDir, latest, "chrome-linux64", "chrome");
43
+ return fs.existsSync(candidate) ? candidate : null;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
26
49
  /**
27
50
  * Spawn a child process and return a promise
28
51
  * @param {string} command The command to run
@@ -67,7 +90,8 @@ export default async (pluginConfig = {}, context = {}) => {
67
90
  }
68
91
 
69
92
  // Check if OS dependency installation is disabled
70
- if (pluginConfig.skipOsDeps === true) {
93
+ const cfg = resolveConfig(context);
94
+ if (pluginConfig.skipOsDeps === true || cfg.skipOsDeps) {
71
95
  debug("OS dependency installation skipped (skipOsDeps=true)");
72
96
  console.log("ℹ️ Skipping OS dependency installation");
73
97
  return;
@@ -77,23 +101,42 @@ export default async (pluginConfig = {}, context = {}) => {
77
101
  debug("Installing Chromium/Chrome OS dependencies for puppeteer...");
78
102
  if (await isDebianLike()) {
79
103
  try {
80
- debug("Detected Debian/Ubuntu system, installing google-chrome-stable via apt-get");
81
-
82
- // Update package lists and install Google Chrome stable
83
- await spawnPromise("sudo", ["apt-get", "update"]);
84
- await spawnPromise("sudo", ["apt-get", "install", "-y", "google-chrome-stable"]);
85
- debug("Chromium/Chrome dependencies installed successfully");
86
-
87
- // Set Chrome executable path for Puppeteer if configured
88
- const chromePath = "/usr/bin/google-chrome-stable";
89
- if (!process.env.PUPPETEER_EXECUTABLE_PATH) {
90
- process.env.PUPPETEER_EXECUTABLE_PATH = chromePath;
91
- debug("Set PUPPETEER_EXECUTABLE_PATH to %s", chromePath);
104
+ debug("Detected Debian/Ubuntu system, installing Chrome (for testing) + OS deps for Puppeteer");
105
+
106
+ const cacheDir = resolveCacheDir(context);
107
+ process.env.PUPPETEER_CACHE_DIR = cacheDir;
108
+
109
+ const envPath = process.env.PATH || "";
110
+ const home = process.env.HOME || "";
111
+ const envPrefix = ["env", `PATH=${envPath}`, `HOME=${home}`, `PUPPETEER_CACHE_DIR=${cacheDir}`];
112
+
113
+ if (Array.isArray(pluginConfig.osDepsCommand) && pluginConfig.osDepsCommand.length > 0) {
114
+ await spawnPromise("sudo", [...envPrefix, ...pluginConfig.osDepsCommand]);
92
115
  } else {
93
- debug("PUPPETEER_EXECUTABLE_PATH already set to %s", process.env.PUPPETEER_EXECUTABLE_PATH);
116
+ // Straightforward: let Puppeteer's installer handle both Chrome + OS deps.
117
+ await spawnPromise("sudo", [
118
+ ...envPrefix,
119
+ "pnpm",
120
+ "dlx",
121
+ "puppeteer",
122
+ "browsers",
123
+ "install",
124
+ "chrome",
125
+ "--install-deps",
126
+ ]);
94
127
  }
128
+
129
+ const execPath = resolveInstalledChromeExecutable(cacheDir);
130
+ if (execPath) {
131
+ process.env.PUPPETEER_EXECUTABLE_PATH = execPath;
132
+ debug("Resolved Chrome executable: %s", execPath);
133
+ }
134
+
135
+ debug("PUPPETEER_CACHE_DIR: %s", process.env.PUPPETEER_CACHE_DIR);
136
+ debug("PUPPETEER_EXECUTABLE_PATH: %s", process.env.PUPPETEER_EXECUTABLE_PATH);
137
+ debug("Chrome/Chromium OS dependencies installed successfully");
95
138
  } catch (error) {
96
- debug("Warning: Could not install google-chrome-stable via apt-get: %s", error.message);
139
+ debug("Warning: Could not install Chrome/Chromium OS dependencies: %s", error.message);
97
140
  console.warn(
98
141
  `⚠️ Warning: Failed to install some OS dependencies: ${error.message}\n` +
99
142
  "⚠️ Some dependencies may need to be installed manually or puppeteer may fall back to downloading Chromium"
package/lib/puppet.js CHANGED
@@ -1,14 +1,97 @@
1
- import puppeteer from "puppeteer";
2
1
  import resolveConfig from "./resolve-config.js";
3
2
  import debugConfig from "debug";
4
3
  import path from "path";
4
+ import fs from "fs";
5
+ import os from "os";
5
6
  import { fileURLToPath } from "url";
6
7
  import { waitForNavigationOrSelector, robustType, wait, safeClose } from "./puppet-utils.js";
8
+
7
9
  const debug = debugConfig("semantic-release:whmcs");
8
10
 
9
11
  const __filename = fileURLToPath(import.meta.url);
10
12
  const __dirname = path.dirname(__filename);
11
13
  export default async (context) => {
14
+ // Set PUPPETEER_CACHE_DIR BEFORE importing puppeteer
15
+ const workDir = process.env.PROJECT_WORKDIR || process.cwd();
16
+ if (!process.env.PUPPETEER_CACHE_DIR) {
17
+ process.env.PUPPETEER_CACHE_DIR = path.join(workDir, ".cache", "puppeteer");
18
+ }
19
+
20
+ debug("PROJECT_WORKDIR: %s", process.env.PROJECT_WORKDIR);
21
+ debug("PUPPETEER_CACHE_DIR: %s", process.env.PUPPETEER_CACHE_DIR);
22
+ debug("PUPPETEER_EXECUTABLE_PATH: %s", process.env.PUPPETEER_EXECUTABLE_PATH);
23
+
24
+ // Check if cache dir exists and list contents
25
+ const cacheDir = process.env.PUPPETEER_CACHE_DIR;
26
+ if (fs.existsSync(cacheDir)) {
27
+ debug("Cache dir contents: %s", fs.readdirSync(cacheDir).join(", "));
28
+ const chromeDir = path.join(cacheDir, "chrome");
29
+ if (fs.existsSync(chromeDir)) {
30
+ debug("Chrome dir contents: %s", fs.readdirSync(chromeDir).join(", "));
31
+ }
32
+ } else {
33
+ debug("Cache dir does NOT exist: %s", cacheDir);
34
+ }
35
+
36
+ const puppeteer = (await import("puppeteer")).default;
37
+
38
+ // Get executable path - prefer env var, then puppeteer's default
39
+ let executablePath = process.env.PUPPETEER_EXECUTABLE_PATH;
40
+ if (!executablePath) {
41
+ executablePath = puppeteer.executablePath();
42
+ debug("Using puppeteer.executablePath(): %s", executablePath);
43
+ }
44
+
45
+ // Check if executable exists
46
+ if (!fs.existsSync(executablePath)) {
47
+ // Try to find Chrome in the configured cache dir
48
+ const chromeDir = path.join(cacheDir, "chrome");
49
+ let found = false;
50
+ if (fs.existsSync(chromeDir)) {
51
+ const versions = fs.readdirSync(chromeDir);
52
+ debug("Available Chrome versions: %s", versions.join(", "));
53
+ if (versions.length > 0) {
54
+ const latestVersion = versions.sort().pop();
55
+ const possiblePath = path.join(chromeDir, latestVersion, "chrome-linux64", "chrome");
56
+ if (fs.existsSync(possiblePath)) {
57
+ executablePath = possiblePath;
58
+ debug("Found Chrome at: %s", executablePath);
59
+ found = true;
60
+ }
61
+ }
62
+ }
63
+
64
+ if (!found) {
65
+ // Fallback: Check default cache locations (home dir)
66
+ // This handles cases where PROJECT_WORKDIR is set but prepare step didn't run or install to project cache
67
+ const homeCache = path.join(os.homedir(), ".cache", "puppeteer", "chrome");
68
+ if (fs.existsSync(homeCache)) {
69
+ debug("Checking fallback cache at: %s", homeCache);
70
+ const versions = fs.readdirSync(homeCache).filter((v) => v.startsWith("linux-") || v.startsWith("chrome-"));
71
+ if (versions.length > 0) {
72
+ const latestVersion = versions.sort().pop();
73
+ const possiblePath = path.join(homeCache, latestVersion, "chrome-linux64", "chrome");
74
+ if (fs.existsSync(possiblePath)) {
75
+ executablePath = possiblePath;
76
+ debug("Found Chrome in fallback location: %s", executablePath);
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ if (!fs.existsSync(executablePath)) {
84
+ throw new Error(
85
+ `Chrome executable not found at: ${executablePath}\n` +
86
+ `PUPPETEER_CACHE_DIR: ${cacheDir} (exists: ${fs.existsSync(cacheDir)})\n` +
87
+ `PUPPETEER_EXECUTABLE_PATH: ${process.env.PUPPETEER_EXECUTABLE_PATH}\n` +
88
+ `PROJECT_WORKDIR: ${process.env.PROJECT_WORKDIR}\n` +
89
+ `process.cwd(): ${process.cwd()}`
90
+ );
91
+ }
92
+
93
+ debug("Final Chrome executable: %s", executablePath);
94
+
12
95
  let config;
13
96
  const cfg = {
14
97
  urlbase: "https://marketplace.whmcs.com",
@@ -51,9 +134,10 @@ export default async (context) => {
51
134
  }
52
135
 
53
136
  const browser = await puppeteer.launch({
54
- headless: cfg.headless === "1" ? true : false,
137
+ headless: cfg.headless,
55
138
  defaultViewport: null, // automatically full-sized
56
139
  args: launchArgs,
140
+ executablePath,
57
141
  });
58
142
  const { logger } = cfg;
59
143
  const [page] = await browser.pages();
@@ -7,9 +7,10 @@ export default (context) => {
7
7
  minversion: env.WHMCSMP_MINVERSION || "7.10",
8
8
  ghtoken: env.GH_TOKEN || env.GITHUB_TOKEN || false,
9
9
  ghrepo: env.GH_REPO || env.GITHUB_REPO || false,
10
- headless: env.PUPPETEER_HEADLESS || "1",
10
+ headless: env.PUPPETEER_HEADLESS !== "0" && env.PUPPETEER_HEADLESS !== "false" && env.PUPPETEER_HEADLESS !== false,
11
11
  debug: (env.DEBUG && /^semantic-release:(\*|whmcs)$/.test(env.DEBUG)) || false,
12
12
  useCookieExtension: env.USE_COOKIE_EXTENSION || true,
13
13
  keepBrowserOpenOnError: env.PUPPETEER_KEEP_OPEN === "1" || env.PUPPETEER_KEEP_OPEN === "true" || false,
14
+ skipOsDeps: env.SKIP_OS_DEPS === "1" || env.SKIP_OS_DEPS === "true" || false,
14
15
  };
15
16
  };
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": "5.2.8",
4
+ "version": "5.2.10",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "publishConfig": {
@@ -37,6 +37,9 @@
37
37
  "files": [
38
38
  "test/**/*.test.js"
39
39
  ],
40
+ "require": [
41
+ "dotenv/config"
42
+ ],
40
43
  "nodeArguments": [
41
44
  "--no-warnings"
42
45
  ],
@@ -68,7 +71,8 @@
68
71
  "overrides": {
69
72
  "http-cache-semantics": "^4.1.1",
70
73
  "word-wrap": "npm:@aashutoshrathi/word-wrap@1.2.6",
71
- "glob-parent": "^5.1.2"
74
+ "glob-parent": "^5.1.2",
75
+ "tar": "^7.5.7"
72
76
  },
73
77
  "devDependencies": {
74
78
  "@semantic-release/changelog": "^6.0.3",
@@ -76,8 +80,9 @@
76
80
  "@semantic-release/git": "^10.0.1",
77
81
  "ava": "6.4.1",
78
82
  "c8": "^10.1.3",
79
- "prettier": "^3.7.4",
80
- "semantic-release": "^25.0.2",
83
+ "dotenv": "^16.6.1",
84
+ "prettier": "^3.8.1",
85
+ "semantic-release": "^25.0.3",
81
86
  "semantic-release-teams-notify-plugin": "github:centralnicgroup-opensource/rtldev-middleware-semantic-release-notify-plugin",
82
87
  "stream-buffers": "^3.0.3"
83
88
  },
@@ -86,7 +91,7 @@
86
91
  "@semantic-release/error": "^4.0.0",
87
92
  "aggregate-error": "^5.0.0",
88
93
  "debug": "^4.4.3",
89
- "puppeteer": ">=23.4.0",
94
+ "puppeteer": "^24.37.2",
90
95
  "yargs": "^18.0.0"
91
96
  }
92
97
  }