cht-conf 3.16.0 → 3.16.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.
- package/CHANGELOG.md +14 -0
- package/Dockerfile +23 -17
- package/README.md +62 -9
- package/devcontainer.cht-ide/.devcontainer.json +6 -0
- package/devcontainer.cht-ide/README.md +3 -0
- package/package.json +4 -3
- package/src/lib/get-api-url.js +6 -8
- package/src/lib/main.js +57 -57
- package/src/lib/package-lib.js +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [3.16.2](https://github.com/medic/cht-conf/compare/v3.16.1...v3.16.2) (2023-03-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **#533:** compile-app-settings with --debug flag should ignore eslint errors ([5592efe](https://github.com/medic/cht-conf/commit/5592efe0ccee0165fa8690f7d4494e7ed7057beb)), closes [#533](https://github.com/medic/cht-conf/issues/533) [#533](https://github.com/medic/cht-conf/issues/533)
|
|
7
|
+
|
|
8
|
+
## [3.16.1](https://github.com/medic/cht-conf/compare/v3.16.0...v3.16.1) (2023-03-18)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **#524:** Don't display usage() information for commands which run successfully ([5b265e7](https://github.com/medic/cht-conf/commit/5b265e753d035b326ff8d6bac0a9aabc923048fb)), closes [#524](https://github.com/medic/cht-conf/issues/524) [#524](https://github.com/medic/cht-conf/issues/524)
|
|
14
|
+
|
|
1
15
|
# [3.16.0](https://github.com/medic/cht-conf/compare/v3.15.3...v3.16.0) (2023-03-09)
|
|
2
16
|
|
|
3
17
|
|
package/Dockerfile
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
FROM
|
|
2
|
-
MAINTAINER DevOps "devops@medicmobile.org"
|
|
1
|
+
FROM node:16-slim
|
|
3
2
|
|
|
3
|
+
RUN apt update \
|
|
4
|
+
&& apt install --no-install-recommends -y \
|
|
5
|
+
build-essential \
|
|
6
|
+
chromium \
|
|
7
|
+
curl \
|
|
8
|
+
git \
|
|
9
|
+
openssh-client \
|
|
10
|
+
python3-pip \
|
|
11
|
+
python3-setuptools \
|
|
12
|
+
python3-wheel \
|
|
13
|
+
xsltproc \
|
|
14
|
+
# Remove chromium to save space. We only installed it to get the transitive dependencies that are needed
|
|
15
|
+
# when running tests with puppeteer. (puppeteer-chromium-resolver will always download its own version of chromium)
|
|
16
|
+
&& apt remove -y chromium \
|
|
17
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
4
18
|
|
|
5
|
-
RUN
|
|
6
|
-
apt-get update -y && \
|
|
7
|
-
apt-get install --no-install-recommends -y -q \
|
|
8
|
-
build-essential python-setuptools \
|
|
9
|
-
python python-pip python-dev \
|
|
10
|
-
libffi-dev libssl-dev \
|
|
11
|
-
libxml2-dev libxslt1-dev zlib1g-dev \
|
|
12
|
-
git wget python-wheel curl
|
|
19
|
+
RUN python3 -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic
|
|
13
20
|
|
|
14
|
-
RUN
|
|
15
|
-
python -m pip install git+https://github.com/medic/pyxform.git@medic-conf-1.17#egg=pyxform-medic
|
|
21
|
+
RUN npm install -g cht-conf
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
# Using the 1000:1000 user is recommended for VSCode dev containers
|
|
24
|
+
# https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user
|
|
25
|
+
USER node
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
WORKDIR /workdir
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
ENTRYPOINT ["cht"]
|
package/README.md
CHANGED
|
@@ -32,22 +32,75 @@ As Administrator:
|
|
|
32
32
|
|
|
33
33
|
### Docker
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
CHT Conf can also be run from within a Docker container. This is useful if you are already familiar with Docker and do not wish to configure the various dependencies required for developing CHT apps on your local machine. The necessary dependencies are pre-packaged in the Docker image.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
#### Building the image
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
docker run -d --name cht-conf cht-conf:v0
|
|
39
|
+
Install [Docker](https://www.docker.com/). If you are using Windows, you also need to enable the [Windows Subsystem for Linux (WSL2)](https://learn.microsoft.com/en-us/windows/wsl/install) to perform the following steps.
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
Using the terminal (or the WLS shell on Windows: _Start > wsl_), run the following commands to create a new project directory and build the Docker image:
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
```shell
|
|
44
|
+
mkdir -p ~/cht-project
|
|
45
|
+
cd ~/cht-project
|
|
46
|
+
curl https://raw.githubusercontent.com/medic/cht-conf/main/Dockerfile > Dockerfile
|
|
47
|
+
docker build -t cht-ide .
|
|
48
|
+
```
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
#### Using the image
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
The resulting Docker image can be used as a [VSCode Development Container](https://code.visualstudio.com/docs/devcontainers/containers) (easiest) or as a standalone Docker utility.
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
##### VSCode Development Container
|
|
55
|
+
|
|
56
|
+
If you want to develop CHT apps with VSCode, you can use the Docker image as a Development Container. This will allow you to use the `cht-conf` utility and its associated tech stack from within VSCode (without needing to install dependencies like NodeJS on your host system).
|
|
57
|
+
|
|
58
|
+
[Install VSCode](https://code.visualstudio.com/) if you do not have it already.
|
|
59
|
+
|
|
60
|
+
Using the terminal (or the WLS shell on Windows: _Start > wsl_), run the following commands from within your project directory (created above) to download the `.devcontainer.json` config file, install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), and open the project directory in VSCode:
|
|
61
|
+
|
|
62
|
+
```shell
|
|
63
|
+
cd ~/cht-project
|
|
64
|
+
curl -s https://raw.githubusercontent.com/medic/cht-conf/main/devcontainer.cht-ide/.devcontainer.json > .devcontainer.json
|
|
65
|
+
code --install-extension ms-vscode-remote.remote-containers
|
|
66
|
+
code -n .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
When opening VSCode, you may be prompted with the question:
|
|
70
|
+
|
|
71
|
+
> **Do you trust the authors of the files in this folder?**
|
|
72
|
+
|
|
73
|
+
Choose, "Yes, I trust the authors".
|
|
74
|
+
|
|
75
|
+
Open the Command Palette in VSCode (_Ctrl+Shift+P_ or _Cmd+Shift+P_) and select `Reopen in Container`. This will open your workspace inside a container based on the `cht-ide` image. You can use the `cht` commands by opening a terminal in VSCode (_Ctrl+Shift+\`_ or _Cmd+Shift+\`_). If prompted "Do you trust the authors..." choose "Trust Folder & Continue".
|
|
76
|
+
|
|
77
|
+
Run the following command in the VSCode terminal to bootstrap your new CHT project:
|
|
78
|
+
|
|
79
|
+
```shell
|
|
80
|
+
cht initialise-project-layout
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
###### Terminal environment
|
|
84
|
+
|
|
85
|
+
When opening a terminal in VSCode in a development container, the terminal will be running on the _container environment_ by default. This is what gives you access to the various `cht` commands. However, this also means you do NOT have access, within the default VSCode terminal, to commands from your _host environment_. So, for example, you cannot run `docker` commands since Docker is not installed inside the container.
|
|
86
|
+
|
|
87
|
+
To open a terminal running on you _host environment_ in VSCode, open the Command Palette (_Ctrl+Shift+P_ or _Cmd+Shift+P_) and select `Create New Integrated Terminal (Local)`. Just remember that you will NOT be able to run `cht` commands from this terminal since cht-conf is not installed on your host machine.
|
|
88
|
+
|
|
89
|
+
##### Standalone Docker utility
|
|
90
|
+
|
|
91
|
+
If you are not using VSCode, you can use the Docker image as a standalone utility from the command line. Instead of using the `cht ...` command, you can run `docker run -it --rm -v "$PWD":/workdir cht-ide ...`. This will create an ephemeral container with access to your current directory that will run the given cht command. (Do not include the `cht` part of the command, just your desired actions/parameters.)
|
|
92
|
+
|
|
93
|
+
Run the following command inside the project directory to bootstrap your new CHT project:
|
|
94
|
+
|
|
95
|
+
```shell
|
|
96
|
+
docker run -it --rm -v "$PWD":/workdir cht-ide initialise-project-layout
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Note on connecting to a local CHT instance
|
|
100
|
+
|
|
101
|
+
When using `cht-conf` within a Docker container to connect to a CHT instance that is running on your local machine (e.g. a development instance), you cannot use the `--local` flag or `localhost` in your `--url` parameter (since these will be interpreted as "local to the container").
|
|
102
|
+
|
|
103
|
+
It is recommended to run a local CHT instance using the [CHT Docker Helper script](https://docs.communityhealthtoolkit.org/apps/guides/hosting/4.x/app-developer/). You can connect to the resulting `...my.local-ip.co` URL from the Docker container (or the VSCode terminal). (Just make sure the port your CHT instance is hosted on is not blocked by your firewall).
|
|
51
104
|
|
|
52
105
|
## Bash completion
|
|
53
106
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cht-conf",
|
|
3
|
-
"version": "3.16.
|
|
3
|
+
"version": "3.16.2",
|
|
4
4
|
"description": "Configure CHT deployments",
|
|
5
5
|
"main": "./src/lib/main.js",
|
|
6
6
|
"engines": {
|
|
@@ -77,19 +77,20 @@
|
|
|
77
77
|
"@semantic-release/git": "^10.0.0",
|
|
78
78
|
"body-parser": "^1.19.0",
|
|
79
79
|
"chai": "^4.3.4",
|
|
80
|
+
"chai-as-promised": "^7.1.1",
|
|
80
81
|
"chai-exclude": "^2.0.3",
|
|
81
82
|
"chai-shallow-deep-equal": "^1.4.6",
|
|
82
83
|
"chai-xml": "^0.4.0",
|
|
83
84
|
"eslint-plugin-node": "^11.1.0",
|
|
84
85
|
"express": "^4.17.1",
|
|
85
86
|
"express-pouchdb": "^4.2.0",
|
|
87
|
+
"fs-extra": "8.1.0",
|
|
86
88
|
"husky": "^7.0.2",
|
|
87
89
|
"mocha": "^6.2.3",
|
|
88
90
|
"nyc": "^15.1.0",
|
|
89
91
|
"pouchdb-adapter-memory": "^7.2.2",
|
|
90
92
|
"rewire": "^5.0.0",
|
|
91
93
|
"semantic-release": "^18.0.0",
|
|
92
|
-
"sinon": "^7.3.2"
|
|
93
|
-
"fs-extra": "8.1.0"
|
|
94
|
+
"sinon": "^7.3.2"
|
|
94
95
|
}
|
|
95
96
|
}
|
package/src/lib/get-api-url.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
const userPrompt = require('./user-prompt');
|
|
2
2
|
const url = require('url');
|
|
3
|
-
const usage = require('../cli/usage');
|
|
4
3
|
|
|
5
4
|
const emoji = require('./emoji');
|
|
6
|
-
const {
|
|
5
|
+
const { info } = require('./log');
|
|
6
|
+
const usage = require('../cli/usage');
|
|
7
7
|
|
|
8
8
|
const getApiUrl = (cmdArgs, env = {}) => {
|
|
9
9
|
const specifiedModes = [cmdArgs.local, cmdArgs.instance, cmdArgs.url, cmdArgs.archive].filter(mode => mode);
|
|
10
10
|
if (specifiedModes.length !== 1) {
|
|
11
|
-
error('Require exactly one of these parameter: --local --instance --url --archive');
|
|
12
11
|
usage();
|
|
13
|
-
|
|
12
|
+
throw Error('One of these parameters is required: --local --instance --url --archive');
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
if (cmdArgs.user && !cmdArgs.instance) {
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
usage();
|
|
17
|
+
throw Error('The --user switch must be accompanied with --instance');
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
let instanceUrl;
|
|
@@ -26,8 +25,7 @@ const getApiUrl = (cmdArgs, env = {}) => {
|
|
|
26
25
|
// See ./archiving-db.js
|
|
27
26
|
instanceUrl = parseLocalUrl(env.COUCH_URL);
|
|
28
27
|
if (instanceUrl.hostname !== 'localhost') {
|
|
29
|
-
|
|
30
|
-
return false;
|
|
28
|
+
throw Error(`--local was specified but COUCH_URL env var is set to '${instanceUrl.hostname}'. Please use --url for remote servers.`);
|
|
31
29
|
}
|
|
32
30
|
} else if (cmdArgs.instance) {
|
|
33
31
|
const password = userPrompt.question(`${emoji.key} Password: `, { hideEchoBack: true });
|
package/src/lib/main.js
CHANGED
|
@@ -134,71 +134,16 @@ module.exports = async (argv, env) => {
|
|
|
134
134
|
//
|
|
135
135
|
// Build up actions
|
|
136
136
|
//
|
|
137
|
-
let actions = cmdArgs._;
|
|
138
|
-
if (actions.length) {
|
|
139
|
-
const unsupported = actions.filter(a => !supportedActions.includes(a));
|
|
140
|
-
if(unsupported.length) {
|
|
141
|
-
throw new Error(`Unsupported action(s): ${unsupported.join(' ')}`);
|
|
142
|
-
}
|
|
143
|
-
} else {
|
|
144
|
-
actions = !cmdArgs.archive ? defaultActions : defaultArchiveActions;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (cmdArgs['skip-git-check']) {
|
|
148
|
-
actions = actions.filter(a => a !== 'check-git');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
137
|
const skipValidate = cmdArgs['skip-validate'];
|
|
152
|
-
|
|
153
|
-
warn('Skipping all form validation.');
|
|
154
|
-
const validateActions = [
|
|
155
|
-
'validate-app-forms',
|
|
156
|
-
'validate-collect-forms',
|
|
157
|
-
'validate-contact-forms',
|
|
158
|
-
'validate-training-forms',
|
|
159
|
-
];
|
|
160
|
-
actions = actions.filter(action => !validateActions.includes(action));
|
|
161
|
-
} else {
|
|
162
|
-
const addFormValidationIfNecessary = (formType) => {
|
|
163
|
-
const updateFormsIndex = actions.indexOf(`upload-${formType}-forms`);
|
|
164
|
-
if (updateFormsIndex >= 0 && actions.indexOf(`validate-${formType}-forms`) < 0) {
|
|
165
|
-
actions.splice(updateFormsIndex, 0, `validate-${formType}-forms`);
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
addFormValidationIfNecessary('app');
|
|
169
|
-
addFormValidationIfNecessary('collect');
|
|
170
|
-
addFormValidationIfNecessary('contact');
|
|
171
|
-
addFormValidationIfNecessary('training');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
actions = actions.map(actionName => {
|
|
175
|
-
const action = require(`../fn/${actionName}`);
|
|
176
|
-
|
|
177
|
-
if (typeof action.execute !== 'function') {
|
|
178
|
-
throw new Error(`${actionName} has not been implemented correctly: no 'execute' function`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (!Object.hasOwnProperty.call(action, 'requiresInstance')) {
|
|
182
|
-
action.requiresInstance = true;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
action.name = actionName;
|
|
186
|
-
|
|
187
|
-
return action;
|
|
188
|
-
});
|
|
138
|
+
const actions = buildActions(cmdArgs, skipValidate);
|
|
189
139
|
|
|
190
140
|
//
|
|
191
141
|
// Initialize the environment
|
|
192
142
|
//
|
|
193
143
|
const projectName = fs.path.basename(pathToProject);
|
|
194
144
|
|
|
195
|
-
const apiUrl = getApiUrl(cmdArgs, env);
|
|
196
145
|
const requiresInstance = actions.some(action => action.requiresInstance);
|
|
197
|
-
|
|
198
|
-
if (!apiUrl) {
|
|
199
|
-
throw new Error('Failed to obtain a url to the API');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
146
|
+
const apiUrl = requiresInstance && getApiUrl(cmdArgs, env);
|
|
202
147
|
|
|
203
148
|
let extraArgs = cmdArgs['--'];
|
|
204
149
|
if (!extraArgs.length) {
|
|
@@ -252,5 +197,60 @@ module.exports = async (argv, env) => {
|
|
|
252
197
|
}
|
|
253
198
|
};
|
|
254
199
|
|
|
200
|
+
function buildActions(cmdArgs, skipValidate) {
|
|
201
|
+
let actions = cmdArgs._;
|
|
202
|
+
if (actions.length) {
|
|
203
|
+
const unsupported = actions.filter(a => !supportedActions.includes(a));
|
|
204
|
+
if (unsupported.length) {
|
|
205
|
+
usage();
|
|
206
|
+
throw new Error(`Unsupported action(s): ${unsupported.join(' ')}`);
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
actions = !cmdArgs.archive ? defaultActions : defaultArchiveActions;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (cmdArgs['skip-git-check']) {
|
|
213
|
+
actions = actions.filter(a => a !== 'check-git');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if(skipValidate) {
|
|
217
|
+
warn('Skipping all form validation.');
|
|
218
|
+
const validateActions = [
|
|
219
|
+
'validate-app-forms',
|
|
220
|
+
'validate-collect-forms',
|
|
221
|
+
'validate-contact-forms',
|
|
222
|
+
'validate-training-forms',
|
|
223
|
+
];
|
|
224
|
+
actions = actions.filter(action => !validateActions.includes(action));
|
|
225
|
+
} else {
|
|
226
|
+
const addFormValidationIfNecessary = (formType) => {
|
|
227
|
+
const updateFormsIndex = actions.indexOf(`upload-${formType}-forms`);
|
|
228
|
+
if (updateFormsIndex >= 0 && actions.indexOf(`validate-${formType}-forms`) < 0) {
|
|
229
|
+
actions.splice(updateFormsIndex, 0, `validate-${formType}-forms`);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
addFormValidationIfNecessary('app');
|
|
233
|
+
addFormValidationIfNecessary('collect');
|
|
234
|
+
addFormValidationIfNecessary('contact');
|
|
235
|
+
addFormValidationIfNecessary('training');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return actions.map(actionName => {
|
|
239
|
+
const action = require(`../fn/${actionName}`);
|
|
240
|
+
|
|
241
|
+
if (typeof action.execute !== 'function') {
|
|
242
|
+
throw new Error(`${actionName} has not been implemented correctly: no 'execute' function`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (!Object.hasOwnProperty.call(action, 'requiresInstance')) {
|
|
246
|
+
action.requiresInstance = true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
action.name = actionName;
|
|
250
|
+
|
|
251
|
+
return action;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
255
|
// Exists for generic mocking purposes
|
|
256
256
|
const executeAction = action => action.execute();
|
package/src/lib/package-lib.js
CHANGED
|
@@ -64,6 +64,7 @@ module.exports = (pathToProject, entry, baseEslintPath, options = {}) => {
|
|
|
64
64
|
options: {
|
|
65
65
|
baseConfig: baseEslintConfig,
|
|
66
66
|
useEslintrc: true,
|
|
67
|
+
ignore: !options.skipEslintIgnore,
|
|
67
68
|
|
|
68
69
|
// pack the library regardless of the eslint result
|
|
69
70
|
failOnError: false,
|
|
@@ -91,7 +92,7 @@ module.exports = (pathToProject, entry, baseEslintPath, options = {}) => {
|
|
|
91
92
|
info(stats.toString());
|
|
92
93
|
|
|
93
94
|
if (stats.hasErrors()) {
|
|
94
|
-
const hasErrorsNotRelatedToLinting = stats.toJson().errors.some(err => !err.includes('
|
|
95
|
+
const hasErrorsNotRelatedToLinting = stats.toJson().errors.some(err => !err.includes('node_modules/eslint-loader'));
|
|
95
96
|
const shouldHalt = options.haltOnLintMessage || hasErrorsNotRelatedToLinting;
|
|
96
97
|
if (shouldHalt) {
|
|
97
98
|
return reject(Error(`Webpack errors when building ${libName}`));
|