cht-conf 5.2.0 → 5.3.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [5.3.0](https://github.com/medic/cht-conf/compare/v5.2.1...v5.3.0) (2025-09-04)
2
+
3
+
4
+ ### Features
5
+
6
+ * **#512:** add form validation for instance::db-doc ([#737](https://github.com/medic/cht-conf/issues/737)) ([0db687e](https://github.com/medic/cht-conf/commit/0db687eb8f2c5dbb6a2d2e387857af50af81a917)), closes [#512](https://github.com/medic/cht-conf/issues/512)
7
+
8
+ ## [5.2.1](https://github.com/medic/cht-conf/compare/v5.2.0...v5.2.1) (2025-08-27)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **#390:** notify user of new cht-conf versions ([#730](https://github.com/medic/cht-conf/issues/730)) ([9d36c21](https://github.com/medic/cht-conf/commit/9d36c215f5b6406233b856b6348fe949757ab0b8)), closes [#390](https://github.com/medic/cht-conf/issues/390)
14
+
1
15
  # [5.2.0](https://github.com/medic/cht-conf/compare/v5.1.0...v5.2.0) (2025-08-07)
2
16
 
3
17
 
package/README.md CHANGED
@@ -293,24 +293,6 @@ To achieve this, create a file called `settings.inherit.json` in your project's
293
293
  }
294
294
  }
295
295
 
296
- # Fetching logs
297
-
298
- Fetch logs from a CHT v2.x production server.
299
-
300
- This is a standalone command installed alongside `cht-conf`. For usage information, run `cht-logs --help`.
301
-
302
- ## Usage
303
-
304
- cht-logs <instance-name> <log-types...>
305
-
306
- Accepted log types:
307
-
308
- api
309
- couchdb
310
- gardener
311
- nginx
312
- sentinel
313
-
314
296
  # Development
315
297
 
316
298
  To develop a new action or improve an existing one, check the ["Actions" doc](src/fn/README.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cht-conf",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "Configure CHT deployments",
5
5
  "main": "./src/lib/main.js",
6
6
  "engines": {
@@ -16,8 +16,6 @@
16
16
  "bin": {
17
17
  "medic-conf": "src/bin/index.js",
18
18
  "cht": "src/bin/index.js",
19
- "medic-logs": "src/bin/cht-logs.bash",
20
- "cht-logs": "src/bin/cht-logs.bash",
21
19
  "shell-completion-for-medic-conf": "src/bin/shell-completion.js",
22
20
  "shell-completion-for-cht": "src/bin/shell-completion.js",
23
21
  "pngout-medic": "bin/pngout",
@@ -94,6 +92,6 @@
94
92
  "pouchdb-adapter-memory": "^9.0.0",
95
93
  "rewire": "^8.0.0",
96
94
  "semantic-release": "^24.2.4",
97
- "sinon": "^19.0.2"
95
+ "sinon": "^21.0.0"
98
96
  }
99
97
  }
package/src/cli/usage.js CHANGED
@@ -8,9 +8,13 @@ ${bold('NAME')}
8
8
 
9
9
  ${bold('SYNOPSIS')}
10
10
  cht <--local|--instance=instance-name|--url=url>
11
- Or:
11
+
12
+ Or
13
+
12
14
  cht <--local|--instance=instance-name|--url=url|--archive> <actions> <options> -- <params>
13
15
 
16
+ ${underscore('Note')}: If <actions>,<options> or <params> are ${bold('not')} passed but one of --local or --instance or --url ${bold('is')} passed, ${bold('all')} supported actions are called.
17
+
14
18
  ${bold('DESCRIPTION')}
15
19
  This script updates and uploads a project's configuration.
16
20
 
@@ -58,6 +62,9 @@ ${bold('OPTIONS')}
58
62
  --skip-dependency-check
59
63
  Skips checking the version running is set to the same version in the package.json
60
64
 
65
+ --skip-version-check
66
+ Skips the automatic check for new cht-conf versions
67
+
61
68
  --skip-git-check
62
69
  Skips checking the status of the current repository that holds the configuration
63
70
 
@@ -65,12 +72,13 @@ ${bold('OPTIONS')}
65
72
  Skips checking message translations
66
73
 
67
74
  --skip-validate
68
- Skips form validation
75
+ Skips form validation
69
76
 
70
77
  --force
71
- CAN BE DANGEROUS! Passes yes to all commands and any where that would prompt to overwrite changes will overwrite automatically.
78
+ CAN BE DANGEROUS! Passes yes to all commands and any where that would prompt to overwrite changes will overwrite automatically.
72
79
  `);
73
80
  /* eslint-enable max-len */
74
81
  };
75
82
 
76
83
  const bold = text => `\x1b[1m${text}\x1b[0m`;
84
+ const underscore = text => `\x1b[4m${text}\x1b[0m`;
@@ -1,39 +1,35 @@
1
- const info = require('../lib/log').info;
2
1
  const request = require('request-promise-native');
3
- const warn = require('../lib/log').warn;
4
-
5
- module.exports = (options) => {
6
- const current = require('../../package').version;
7
- info(`Current version: ${current}. Checking NPM for updates…`);
8
-
9
- if (!options) {
10
- options = {};
11
- }
2
+ const log = require('../lib/log');
3
+ const current = require('../../package.json').version;
12
4
 
5
+ module.exports = (options = {}) => {
13
6
  return request
14
7
  .get('https://registry.npmjs.org/cht-conf')
15
8
  .then(res => {
16
9
  const json = JSON.parse(res);
17
- const latest = json['dist-tags'].latest;
18
-
19
- if (latest === current) {
20
- info('You are already on the latest version :¬)');
21
- } else {
22
- warn(`New version available!
10
+ return json['dist-tags'].latest;
11
+ })
12
+ .catch(err => {
13
+ log.warn(`Could not check NPM for updates: ${err.message}`);
14
+ if (!options.nonFatal) {
15
+ throw err;
16
+ }
17
+ })
18
+ .then(latest => {
19
+ if (latest && latest !== current) {
20
+ log.warn(`New version available!
23
21
 
24
- ${current} -> ${latest}
22
+ ${current} -> ${latest}
25
23
 
26
- To install:
24
+ To install:
27
25
 
28
- npm install -g cht-conf
29
- `);
30
- }
31
- })
32
- .catch(err => {
33
- if (options.nonFatal && err.cause && err.cause.code === 'ENOTFOUND') {
34
- warn('Could not check NPM for updates. You may be offline.');
35
- } else {
36
- warn(`Could not check NPM for updates. Error: ${err.message}`);
26
+ npm install -g cht-conf
27
+ `);
28
+ if (!options.nonFatal) {
29
+ throw new Error('You are not running the latest version of cht-conf!');
30
+ }
31
+ } else if (!options.nonFatal) {
32
+ log.info('You are already on the latest version :¬)');
37
33
  }
38
34
  });
39
35
  };
@@ -9,6 +9,14 @@ const getNode = (currentNode, path) =>
9
9
  const getNodes = (currentNode, path) =>
10
10
  xpath.parse(path).select({ node: currentNode, allowAnyNamespaceForNoPrefix: true });
11
11
 
12
+ const getFullNodePath = (childNode) => {
13
+ if (childNode.nodeType !== 1) {
14
+ return '';
15
+ }
16
+ const parentPath = getFullNodePath(childNode.parentNode);
17
+ return parentPath + '/' + childNode.nodeName;
18
+ };
19
+
12
20
  module.exports = {
13
21
  /**
14
22
  * Get the full path of the form, or null if the path doesn't exist.
@@ -68,6 +76,15 @@ module.exports = {
68
76
  */
69
77
  getPrimaryInstanceNode: xmlDoc => getNode(xmlDoc, `${XPATH_MODEL}/instance`),
70
78
 
79
+ /**
80
+ * Returns the path to the given child node relative to the primary instance node.
81
+ * This is useful for identifying the node in error/warning messages.
82
+ * @param childNode the child node
83
+ * @returns {string}
84
+ */
85
+ getPrimaryInstanceNodeChildPath: (childNode) => getFullNodePath(childNode)
86
+ .replace(`${XPATH_MODEL}/instance`, ''),
87
+
71
88
  /**
72
89
  * Check whether the XForm has the <instanceID/> tag.
73
90
  * @param {string} xmlDoc the XML document
package/src/lib/main.js CHANGED
@@ -187,11 +187,6 @@ module.exports = async (argv, env) => {
187
187
  info(`Processing config in ${projectName}.`);
188
188
  info('Actions:\n -', actions.map(({name}) => name).join('\n - '));
189
189
 
190
- const skipCheckForUpdates = cmdArgs.check === false;
191
- if (actions.some(action => action.name === 'check-for-updates') && !skipCheckForUpdates) {
192
- await checkForUpdates({ nonFatal: true });
193
- }
194
-
195
190
  for (const action of actions) {
196
191
  info(`Starting action: ${action.name}…`);
197
192
  await executeAction(action);
@@ -201,6 +196,9 @@ module.exports = async (argv, env) => {
201
196
  if (actions.length > 1) {
202
197
  await info('All actions completed.');
203
198
  }
199
+ if (!actions.some(action => action.name === 'check-for-updates') && !cmdArgs['skip-version-check']) {
200
+ await checkForUpdates({ nonFatal: true });
201
+ }
204
202
  };
205
203
 
206
204
  function buildActions(cmdArgs, skipValidate) {
@@ -0,0 +1,27 @@
1
+ const { getPrimaryInstanceNode, getNodes, getPrimaryInstanceNodeChildPath } = require('../../forms-utils');
2
+
3
+ module.exports = {
4
+ name: 'db-doc-is-valid.js',
5
+ requiresInstance: false,
6
+ execute: async ({ xmlDoc, xformPath }) => {
7
+ const errors = [];
8
+ const primaryInstance = getPrimaryInstanceNode(xmlDoc);
9
+ const dbDocNodes = getNodes(xmlDoc, '//*[@db-doc="true"]', primaryInstance);
10
+ dbDocNodes.forEach(node => {
11
+ const childNodes = node.childNodes.filter(n => n.nodeType === 1);
12
+ if (!childNodes.length) {
13
+ const nodeName = getPrimaryInstanceNodeChildPath(node);
14
+ errors.push(` - ${nodeName}: the db-doc attribute must only be set on groups.`);
15
+ return;
16
+ }
17
+ const hasTypeField = childNodes.some(child => child.nodeName === 'type');
18
+ if (!hasTypeField) {
19
+ const nodeName = getPrimaryInstanceNodeChildPath(node);
20
+ errors.push(` - ${nodeName}: groups configured with the db-doc attribute must contain a valid type field.`);
21
+ }
22
+ });
23
+
24
+ const header = `Form at ${xformPath} contains invalid db-doc configuration:`;
25
+ return { errors: errors.length ? [header, ...errors]: errors };
26
+ }
27
+ };
@@ -1,62 +0,0 @@
1
- #!/bin/bash -eu
2
-
3
- usage() {
4
- cat <<EOF
5
- cht-logs
6
- ==========
7
-
8
- Fetch logs from production CHT servers.
9
-
10
- USAGE
11
- $0 <instance-name> <log-types...>
12
-
13
- Accepted log types:
14
-
15
- api
16
- couchdb
17
- gardener
18
- nginx
19
- sentinel
20
- EOF
21
- }
22
-
23
- if [[ $# = 0 ]]; then usage; exit 1; fi
24
- if [[ "$1" = "--help" ]]; then usage; exit; fi
25
- if [[ $# < 2 ]]; then usage; exit 1; fi
26
-
27
- instanceName="$1"
28
- shift
29
-
30
- domain="${instanceName}.medicmobile.org"
31
-
32
- localRoot="fetched-logs/${instanceName}"
33
-
34
- while [[ $# > 0 ]]; do
35
- logType="$1"
36
- shift
37
-
38
- case "$logType" in
39
- nginx) logPath="/var/log/medic-core/nginx"; realLogType=nginx ;;
40
-
41
- gardener) logPath="/var/log/gardener"; realLogType=gardener ;;
42
- api) logPath="/var/log/gardener"; realLogType=gardener ;;
43
- medic-api) logPath="/var/log/gardener"; realLogType=gardener ;;
44
- sentinel) logPath="/var/log/gardener"; realLogType=gardener ;;
45
-
46
- couch) logPath="/var/log/medic-core/couchdb";realLogType=couchdb ;;
47
- couchdb) logPath="/var/log/medic-core/couchdb";realLogType=couchdb ;;
48
-
49
- *) echo "ERROR: Cannot fetch logs of type: $logType"; exit 1 ;;
50
- esac
51
-
52
- localDir="${localRoot}/${realLogType}"
53
- mkdir -p "$localDir"
54
-
55
- echo "[logs:$logType] Fetching logs to ${localDir}..."
56
- rsync -az --progress -e 'ssh -p 33696' "vm@${domain}:${logPath}/*" "$localDir"
57
-
58
- echo "[logs:$logType] Decompressing logs..."
59
- (cd "$localDir" && find . -name \*.bz2 | xargs bzip2 -dk)
60
-
61
- echo "[logs:$logType] Completed. Logs saved in: ${localDir}"
62
- done